///|
struct HydrationHost {
browser : BrowserHost
transcript : Map[String, Json]
}
///|
priv suberror UnwrapNone
///|
pub fn HydrationHost::HydrationHost(builder : GraphBuilder) -> HydrationHost {
let transcript = try {
let element = @dom.document()
.get_element_by_id(RABBITA_TRANSCRIPT_ID)
.to_option()
.unwrap_or_error(UnwrapNone)
let text = element
.get_property("textContent")
.to_option()
.unwrap_or_error(UnwrapNone)
guard @json.parse(text) is Object(transcript) else { fail("") }
transcript
} catch {
_ => Map([])
}
let (host, output) = @duplix.with_scope(scope => {
let host = HydrationHost::{
browser: {
stores: SlotMap(),
scope,
task_queue: Queue([]),
graph_output: None,
document: None,
url_request: _ => @cmd.none,
url_changed: _ => @cmd.none,
paint_scheduled: false,
drain_scheduled: true,
after_render_queue: Queue([]),
},
transcript,
}
(host, ambient_host.protect(host, builder))
})
host.browser.graph_output = Some(output)
let url = @dom.window().current_url()
let cmd = Context::inject_url_changed(host, url)
Scheduler::queue_command(host, cmd)
let vnode = for ;; {
host.drain_task_with_transcript()
let tmp = ambient_host.protect(host, () => output.read())
if host.browser.task_queue.is_empty() {
break tmp
}
}
let vdom = @vdom.VDom::initialize_with_hydration(vnode, host)
host.browser.document = Some(vdom)
host.browser.drain_scheduled = false
host
}
///|
pub impl Host for HydrationHost with fn flush(self) {
Host::flush(self.browser)
}
///|
pub impl Host for HydrationHost with fn get_stores(self) {
self.browser.stores
}
///|
fn HydrationHost::drain_task_with_transcript(host : Self) -> Unit {
let self = host.browser
while self.task_queue.pop() is Some((op, extension)) {
if handle_message(host, extension) {
continue
}
fn fallback() {
try @cmd.invoke(@key.key, op, extension, self) catch {
Unhandled => ()
} noraise {
None => ()
Ready(cmd) => self.queue_command(cmd)
Async(f) =>
@js_async.Promise::from_async(() => {
Scheduler::queue_command(self, f())
})
|> ignore
AfterLayout(f) => self.after_render_queue.push(f)
LegacyEffect(Immediately, f) => f(self)
LegacyEffect(AfterRender, f) =>
self.after_render_queue.push(() => {
f(self)
@cmd.none
})
}
}
try {
let runner = @cmd.runner(@key.key, op)
let key = runner.identify(extension)
let json = host.transcript
.get(key)
.unwrap_or_error(@cmd.HydrateExn::Fallback)
let cmd = runner.resume_(extension, json, host)
self.queue_command(cmd)
} catch {
@cmd.Unhandled::Unhandled
| @cmd.HydrateExn::Unhandled
| @cmd.HydrateExn::Fallback => fallback()
@cmd.HydrateExn::Skip | _ => ()
}
}
}
///|
pub impl Host for HydrationHost with fn cleanup(self) {
self.browser.cleanup()
}
///|
pub impl Scheduler for HydrationHost with fn queue_command(self, cmd) {
self.browser.queue_command(cmd)
}
///|
pub impl Scheduler for HydrationHost with fn set_url_changed_injector(
self,
injector,
) {
self.browser.set_url_changed_injector(injector)
}
///|
pub impl Scheduler for HydrationHost with fn set_url_request_injector(
self,
injector,
) {
self.browser.set_url_request_injector(injector)
}
///|
pub impl Context for HydrationHost with fn get_origin(self) {
self.browser.get_origin()
}
///|
pub impl Context for HydrationHost with fn inject_url_changed(self, url) {
self.browser.inject_url_changed(url)
}
///|
pub impl Context for HydrationHost with fn inject_url_request(self, url) {
self.browser.inject_url_request(url)
}