forked from 2sys/phonograph
93 lines
2.7 KiB
Gleam
93 lines
2.7 KiB
Gleam
import gleam/dynamic.{type Dynamic}
|
|
import gleam/dynamic/decode.{type Decoder}
|
|
import gleam/option.{type Option}
|
|
import lustre/attribute.{type Attribute}
|
|
import lustre/effect.{type Effect}
|
|
import lustre/event
|
|
|
|
/// WARNING: Lifecycle hooks in Lustre are currently limited to non-existent,
|
|
/// so it's not possible to unsubscribe from context updates on component
|
|
/// teardown. Be cautious using this effect with components that are not
|
|
/// expected to be permanent fixtures on a page. (Refer to
|
|
/// https://github.com/lustre-labs/lustre/issues/320.)
|
|
pub fn request_context(
|
|
context context: Dynamic,
|
|
subscribe subscribe: Bool,
|
|
decoder decoder: Decoder(msg),
|
|
) -> Effect(msg) {
|
|
use dispatch, root <- effect.before_paint
|
|
use value, _unsubscribe <- emit_context_request(root, context, subscribe)
|
|
case decode.run(value, decoder) {
|
|
Ok(msg) -> {
|
|
dispatch(msg)
|
|
}
|
|
Error(_) -> Nil
|
|
}
|
|
Nil
|
|
}
|
|
|
|
@external(javascript, "./context.ffi.mjs", "emitContextRequest")
|
|
fn emit_context_request(
|
|
root _root: Dynamic,
|
|
context _context: Dynamic,
|
|
subscribe _subscribe: Bool,
|
|
callback _callback: fn(Dynamic, Option(fn() -> Nil)) -> Nil,
|
|
) -> Nil {
|
|
Nil
|
|
}
|
|
|
|
/// Capture "context-request" events querying a particular context type.
|
|
/// Intended to be used in a context provider, on a child element near the
|
|
/// component root.
|
|
pub fn on_context_request(
|
|
context match_context: Dynamic,
|
|
handler handler: fn(Dynamic, Bool) -> msg,
|
|
) -> Attribute(msg) {
|
|
event.advanced("context-request", {
|
|
use ev_context <- decode.field("context", decode.dynamic)
|
|
use subscribe <- decode.field("subscribe", decode.bool)
|
|
use callback <- decode.field("callback", decode.dynamic)
|
|
case ev_context == match_context {
|
|
True -> {
|
|
decode.success(event.handler(
|
|
handler(callback, subscribe),
|
|
False,
|
|
ev_context == match_context,
|
|
))
|
|
}
|
|
False ->
|
|
// returning a DecodeError seems like the only way to not trigger a message dispatch
|
|
decode.failure(
|
|
event.handler(handler(callback, subscribe), False, False),
|
|
"Context",
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Effect for passing context value back to consumers who have requested it.
|
|
pub fn update_consumers(
|
|
callbacks callbacks: List(Dynamic),
|
|
value value: Dynamic,
|
|
) -> Effect(msg) {
|
|
use _dispatch <- effect.from
|
|
do_update_consumers(callbacks:, value:)
|
|
}
|
|
|
|
fn do_update_consumers(
|
|
callbacks callbacks: List(Dynamic),
|
|
value value: Dynamic,
|
|
) -> Nil {
|
|
case callbacks {
|
|
[] -> Nil
|
|
[cb, ..rest] -> {
|
|
call_callback(callback: cb, value:)
|
|
do_update_consumers(rest, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
@external(javascript, "./context.ffi.mjs", "callCallback")
|
|
fn call_callback(callback _callback: Dynamic, value _value: Dynamic) -> Nil {
|
|
Nil
|
|
}
|