-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create - chat_input component #5
Comments
tasks -
|
feedback alright. looks good. few things i noticed in the communication, that should change. So the idea is the following:
RULES:
The This way, we will have for each user (bob, ana, ...), a data structure, The visible chat log view in the DOM needs to use that data structure to Now of course, how a specific kind of message is "visualized" in the chat view, It might be good to be able to e.g. "highlight" other messages in the log, Of course, a single message can contain more than one past message through If the order indicated by |
feedback A general convention for how to use html and sub components would be to create either a "list container" to append a list of sub component instances or a "named element container" to append a singleton instance of a sub component. It would be great if we could bit by bit really strictly follow that convention. Components will be re-used in different contexts and one big reason why we write components the way we do is that they have some sort of membrane around themselves to protect others and themselves from accidentally or maliciously interfering with each other in non-consent ways :-) exampleAn example of a list container would be the An example of a singleton element container would be some element , maybe Currently we just append the input sub component instance to the
Lines 46 to 79 in 5c56174
What does (2.) mean? function inputfield (...) {
// ...
const el = document.createElement('div')
const sh = el.attachShadow({ mode: 'closed' })
setTimeout(foo, 1000) // @TODO: will explain below
// ...
return el
} The container component can const inputfield = require('input-field')
function chat (...) {
const el = document.createElement('div')
el.innerHTML = `<div class="chat">
<header> bob </header>
<div class="history"></div>
</div>`
el.append(inputfield())
return el
} now what if: function foo () { // code used in the first sub component
const [header, history] = el.parent.children
header.innerHTML = 'evil-hacker'
history.innerHTML = 'you have been hacked!'
} How can this be prevented? const inputfield = require('input-field')
function chat (...) {
const el = document.createElement('div')
el.innerHTML = `<div class="chat">
<header> bob </header>
<div class="history"></div>
<div class="input"></div>
</div>`
const shopts = { mode: 'closed' }
const input = el.querySelector('input').attachShadow(shopts)
input.append(inputfield())
return el
} This simple change basically will not allow the sub component instance to get access to header or history, because it only has access to the content inside the empty shadow of the designated .input element shadow it gets appended to. instantiating sub componentsi see the following code in your code (simplified): async function task_messenger (opts, protocol) {
// ...
{ // task explorer
// ...
const element = task_explorer(opts = { users, host: username }, protocol)
// ...
}
{ // chat input
// ...
const element = await chat_input(opts = { users, host: username }, protocol)
// ...
}
} Yes, the above technically works. async function task_messenger (opts, protocol) {
// ...
{ // task explorer
// ...
const opts = { users, host: username }
const element = task_explorer(opts, protocol)
// ...
}
{ // chat input
// ...
const opts = { users, host: username }
const element = await chat_input(opts, protocol)
// ...
}
} This basically does the exact same thing, but now javascript creates a nested Lines 78 to 79 in 5c56174
|
feedback refactoring / code review stuffFor example the following code we currently have in Lines 94 to 123 in 5c56174
And if we had the above, we definitely needed to change it, for example like the following: async function post_msg (message) {
render_msg(messsage, 'right') // action "side effect"
const channel = state.net[state.aka.task_explorer]
channel.send({
head: [id, channel.send.id, channel.mid++],
type: 'post_msg',
data: { content, username, chat_id: chat.id }
})
channel_up.send({
head: [id, channel_up.send.id, channel_up.mid++],
type: 'send',
data: {from: username, users, to: 'task_messenger', type: 'on_post_msg', data_re: {content: content, chat_id: chat.id}}
})
}
// ---
function render_msg ({ data }, ...classes) {
const { content, username } = data
const element = document.createElement('div')
element.classList.add('msg', ...classes)
if (username === 'system') {
element.classList.add('system')
element.innerHTML = content
} else {
element.innerHTML = `
<div class="username">${username}</div>${content}
`
}
history.append(element)
history.scrollTop = history.scrollHeight
} This already will allow us to also change the next function too async function on_post_msg (data) {
const { from, data_re } = data
const { content, chat_id } = data_re
const channel = state.net[state.aka.task_explorer]
const message = {
head: [id, channel.send.id, channel.mid++],
type: 'post_msg',
data: { content, username: from, chat_id }
}
channel.send(message)
if (chat && chat_id === chat.id) render_msg(msg)
} Maybe we can rename
open questions:An open question is where should we STORE the history of messages we send and receive? There is the "chat view", which updates based on what task is selected. Of course the "task messenger component" which updates the task view should probably store all messages based on chat rooms... ...but then again, that would be multiple lists per task, one for each peer in the chat (e.g. bob, ana, eve, ...) An alternative would be to store the chat history lists (=arrays) inside each task component instance and when when a different task becomes active, then we retrieve the relevant messages for the "chat view" from there? ... That way, a specific task would only have access to store/read/do anything with it's own messages related to it's own task AND it could create further "views" or sub db intsances for further nested sub tasks. When a different task gets focused by the user: Please share your recommendations as well. In the end, when we try to implement it and do the scrolling and lazy loading and updating of the "chat view", we will probably notice what data structures lend themselves best and how for example a taskdb API should look like to support what we need smoothly :-) Basically the following would be touched, even after refactoring: async function open_chat ({ data }){
chat = { data: data.chat, id: data.id }
history.innerHTML = ''
chat.data.forEach(msg => { // @INFO: here!
const classes = []
if (msg.username === username) classes.push('right')
render_msg(messsage, ...classes)
})
const channel = state.net[state.aka.chat_input]
channel.send({
head: [id, channel.send.id, channel.mid++],
type: 'activate_input',
data: 'Type a message'
})
update_status(data)
}
// and separate function
function update_status (data) {
const title = footer.querySelector('.title')
title.innerHTML = data.name
const input = footer.querySelector('.input')
input.innerHTML = `Inputs: ${data.inputs ? data.inputs.length : '0'}`
const output = footer.querySelector('.output')
output.innerHTML = `Outputs: ${data.outputs ? data.outputs.length : '0'}`
const task = footer.querySelector('.task')
task.innerHTML = `Tasks: ${data.tasks ? data.tasks.length : '0'}`
} Above, where the comment says In this context WE SHOULD differentiate between
Now of course, this can be an internal part of The source lists can always be reprocessed (replayed) from first to last to ideally deterministically re-generate the finaly: chat input componentall components, not just this one should utilize the task-messenger/src/node_modules/chat_input/chat_input.js Lines 21 to 24 in 5c56174
which should probably be rewritten to the following: async function chat_input (opts, protocol) {
// ----------------------------------------
// ID + JSON STATE
// ----------------------------------------
const id = `${ID}:${count++}` // assigns their own name
const status = {}
const state = STATE.ids[id] = { id, status, wait: {}, net: {}, aka: {} } // all state of component instance
status.name = 'chat_input'
status.shift_status = true
status.textmode = "msg"
status.username = opts.host
// ----------------------------------------
// PROTOCOL
// ----------------------------------------
// ...
// ...
} A strong indicator would have even been, that you already called it Now technically, we could also make up some more properties of the The same technically applies to function task_explorer (opts, protocol) {
// ----------------------------------------
// ID + JSON STATE
// ----------------------------------------
const id = `${ID}:${count++}` // assigns their own name
const status = {}
const state = STATE.ids[id] = { id, status, wait: {}, net: {}, aka: {} } // all state of component instance
const { host } = opts
const name = 'task_explorer'
let selected_task, query_result
let chat_task, result, track
const code_words = {inputs: 'io', outputs: 'io', tasks: 'task'}
const add_words = {tasks: 'sup_tasks'}
// ----------------------------------------
// ...
} to // ----------------------------------------
const code_words = {inputs: 'io', outputs: 'io', tasks: 'task'}
const add_words = {tasks: 'sup_tasks'}
// ----------------------------------------
function task_explorer (opts, protocol) {
// ----------------------------------------
// ID + JSON STATE
// ----------------------------------------
const id = `${ID}:${count++}` // assigns their own name
const status = {}
const state = STATE.ids[id] = { id, status, wait: {}, net: {}, aka: {} } // all state of component instance
status.host = opts.host
status.name = 'task_explorer'
status.selected_task = null
status.query_result = null
status.chat_task = null
status.result = null
status.track = null
// ----------------------------------------
// ...
} So basically, what is actually part of a component instance, stays inside the component instance, e.g. on naming stuff const check = parent.children.length ? false : true Saw this line and generally, a good convention we started using in those cases is, to use variable names which start either with It is really difficult to understand what So, rule of thumb would be to always choose names as specific as possible. As little abstract as possible. Words that are more abstract as necessary are technically always correct, but practically will make you wonder what is happening and you need to read a lot of code to understand it instead of being able to guess it from the name alone. Also, if we can maybe take more time to name things the way that most closely resemble what they are - i know naming is hard, it always takes some extra time to think about the name. For example, If i read "tasks", i imagine probably a list of elements or an array of task describing json data or a bullet point list of todos or something, so it needs additional cognitive load or effort to translate it to what it is. |
|
Todo
@output
📦 chat_input_v0.0.1 from commentThe text was updated successfully, but these errors were encountered: