LiveView Native Clients: Support streaming
KronicDeth opened this issue · comments
Port support for streams from phoenixframework/phoenix_live_view#2423
The wire format does have a new stream
key that the diff/merge will need to handle.
// Rendered
export const DYNAMICS = "d"
export const STATIC = "s"
export const COMPONENTS = "c"
export const EVENTS = "e"
export const REPLY = "r"
export const TITLE = "t"
export const TEMPLATES = "p"
export const STREAM = "stream"
It is used in comprehensionToBuffer
where previously just DYNAMICS
and STATIC
was done.
comprehensionToBuffer(rendered, templates, output){
let {[DYNAMICS]: dynamics, [STATIC]: statics, [STREAM]: stream} = rendered
let [_ref, _inserts, deleteIds, reset] = stream || [null, {}, [], null]
statics = this.templateStatic(statics, templates)
let compTemplates = templates || rendered[TEMPLATES]
for(let d = 0; d < dynamics.length; d++){
let dynamic = dynamics[d]
output.buffer += statics[0]
for(let i = 1; i < statics.length; i++){
this.dynamicToBuffer(dynamic[i - 1], compTemplates, output)
output.buffer += statics[i]
}
}
if(stream !== undefined && (rendered[DYNAMICS].length > 0 || deleteIds.length > 0 || reset)){
delete rendered[STREAM]
output.streams.add(stream)
}
}
The whole of rendered.js
changed from output just a rendered string to also tracking and outputting the streams too
let output = {buffer: "", components: components, onlyCids: onlyCids, streams: new Set()}
toString(onlyCids){
let [str, streams] = this.recursiveToString(this.rendered, this.rendered[COMPONENTS], onlyCids)
return [str, streams]
}
Rendered.toString()
is called in renderContainer
renderContainer(diff, kind){
return this.liveSocket.time(`toString diff (${kind})`, () => {
let tag = this.el.tagName
// Don't skip any component in the diff nor any marked as pruned
// (as they may have been added back)
let cids = diff ? this.rendered.componentCIDs(diff).concat(this.pruningCIDs) : null
let [html, streams] = this.rendered.toString(cids)
return [`<${tag}>${html}</${tag}>`, streams]
})
}
renderContainer
is called and the output put into a DOMPatch
} else if(!isEmpty(diff)){
this.liveSocket.time("full patch complete", () => {
let [html, streams] = this.renderContainer(diff, "update")
let patch = new DOMPatch(this, this.el, this.id, html, streams, null)
phxChildrenAdded = this.performPatch(patch, true)
})
}
DOMPatch.perform()
then applies the streams
changes before the normal morphdom
algorithm
this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
Object.entries(inserts).forEach(([key, [streamAt, limit]]) => {
this.streamInserts[key] = {ref, streamAt, limit}
})
if(reset !== undefined){
DOM.all(container, `[${PHX_STREAM_REF}="${ref}"]`, child => {
this.removeStreamChildElement(child)
})
}
deleteIds.forEach(id => {
let child = container.querySelector(`[id="${id}"]`)
if(child){ this.removeStreamChildElement(child) }
})
})
@Bajix do we have any news here? Thanks a lot
FYI @KronicDeth
Blocked by /issues/16