liveview-native / liveview-native-core

Provides core language-agnostic functionality for LiveView Native across platforms

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

https://github.com/phoenixframework/phoenix_live_view/blob/45bd9bd23dcd4524a328950f511ff30f8382c4dd/assets/js/phoenix_live_view/rendered.js#L44

   let output = {buffer: "", components: components, onlyCids: onlyCids, streams: new Set()}

https://github.com/phoenixframework/phoenix_live_view/blob/45bd9bd23dcd4524a328950f511ff30f8382c4dd/assets/js/phoenix_live_view/rendered.js#L37-L40

  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