-module(sprocket@context). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([callback_param_from_string/1, dep/1, compare_deps/2, new/5, prepare_for_reconciliation/1, fetch_or_init_hook/2, update_hook/3, push_event_handler/2, get_event_handler/2, dispatch_event/4, provider/3]). -export_type([callback_param/0, identifiable_handler/0, attribute/0, element/0, updater/1, dispatcher/0, hook_trigger/0, effect_result/0, callback_result/0, memo_result/0, hook/0, compared/1, component_wip/0, context/0]). -type callback_param() :: {callback_string, binary()}. -type identifiable_handler() :: {identifiable_handler, sprocket@internal@utils@unique:unique(), fun((gleam@option:option(callback_param())) -> nil)}. -type attribute() :: {attribute, binary(), gleam@dynamic:dynamic_()} | {event, binary(), identifiable_handler()} | {client_hook, sprocket@internal@utils@unique:unique(), binary()}. -type element() :: {element, binary(), list(attribute()), list(element())} | {component, fun((context(), gleam@dynamic:dynamic_()) -> {context(), element()}), gleam@dynamic:dynamic_()} | {fragment, list(element())} | {debug, binary(), gleam@option:option(gleam@dynamic:dynamic_()), element()} | {keyed, binary(), element()} | {ignore_update, element()} | {provider, binary(), gleam@dynamic:dynamic_(), element()} | {text, binary()}. -type updater(IAW) :: {updater, fun((IAW) -> {ok, nil} | {error, nil})}. -type dispatcher() :: {dispatcher, fun((binary(), binary(), gleam@option:option(binary())) -> {ok, nil} | {error, nil})}. -type hook_trigger() :: on_mount | on_update | {with_deps, list(gleam@dynamic:dynamic_())}. -type effect_result() :: {effect_result, gleam@option:option(fun(() -> nil)), gleam@option:option(list(gleam@dynamic:dynamic_()))}. -type callback_result() :: {callback_result, gleam@option:option(list(gleam@dynamic:dynamic_()))}. -type memo_result() :: {memo_result, gleam@option:option(list(gleam@dynamic:dynamic_()))}. -type hook() :: {callback, sprocket@internal@utils@unique:unique(), fun(() -> nil), gleam@option:option(callback_result())} | {memo, sprocket@internal@utils@unique:unique(), gleam@dynamic:dynamic_(), gleam@option:option(memo_result())} | {effect, sprocket@internal@utils@unique:unique(), fun(() -> gleam@option:option(fun(() -> nil))), hook_trigger(), gleam@option:option(effect_result())} | {handler, sprocket@internal@utils@unique:unique(), fun((gleam@option:option(callback_param())) -> nil)} | {reducer, sprocket@internal@utils@unique:unique(), gleam@dynamic:dynamic_(), fun(() -> nil)} | {state, sprocket@internal@utils@unique:unique(), gleam@dynamic:dynamic_()} | {client, sprocket@internal@utils@unique:unique(), binary(), gleam@option:option(fun((binary(), gleam@option:option(gleam@dynamic:dynamic_()), fun((binary(), gleam@option:option(binary())) -> {ok, nil} | {error, nil})) -> nil))}. -type compared(IAX) :: {changed, IAX} | unchanged. -type component_wip() :: {component_wip, sprocket@internal@utils@ordered_map:ordered_map(integer(), hook()), integer(), boolean()}. -type context() :: {context, element(), component_wip(), list(identifiable_handler()), fun(() -> nil), fun((sprocket@internal@utils@unique:unique(), fun((hook()) -> hook())) -> nil), fun((sprocket@internal@utils@unique:unique(), binary(), gleam@option:option(binary())) -> {ok, nil} | {error, nil}), gleam@erlang@process:subject(ids@cuid:message()), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())}. -spec callback_param_from_string(binary()) -> callback_param(). callback_param_from_string(Value) -> {callback_string, Value}. -spec dep(any()) -> gleam@dynamic:dynamic_(). dep(Dependency) -> gleam@dynamic:from(Dependency). -spec compare_deps( list(gleam@dynamic:dynamic_()), list(gleam@dynamic:dynamic_()) ) -> compared(list(gleam@dynamic:dynamic_())). compare_deps(Prev_deps, Deps) -> case gleam@list:strict_zip(Prev_deps, Deps) of {error, length_mismatch} -> sprocket@internal@exceptions:throw_on_unexpected_deps_mismatch( {<<"compare_deps"/utf8>>, Prev_deps, Deps} ); {ok, Zipped_deps} -> case gleam@list:all( Zipped_deps, fun(Z) -> {A, B} = Z, A =:= B end ) of true -> unchanged; _ -> {changed, Deps} end end. -spec new( element(), gleam@erlang@process:subject(ids@cuid:message()), gleam@option:option(dispatcher()), fun(() -> nil), fun((sprocket@internal@utils@unique:unique(), fun((hook()) -> hook())) -> nil) ) -> context(). new(View, Cuid_channel, Dispatcher, Render_update, Update_hook) -> {context, View, {component_wip, sprocket@internal@utils@ordered_map:new(), 0, true}, [], Render_update, Update_hook, fun(Id, Name, Payload) -> case Dispatcher of {some, {dispatcher, Dispatch}} -> Dispatch( sprocket@internal@utils@unique:to_string(Id), Name, Payload ); none -> {error, nil} end end, Cuid_channel, gleam@dict:new()}. -spec prepare_for_reconciliation(context()) -> context(). prepare_for_reconciliation(Ctx) -> erlang:setelement(4, Ctx, []). -spec fetch_or_init_hook(context(), fun(() -> hook())) -> {context(), hook(), integer()}. fetch_or_init_hook(Ctx, Init) -> Index = erlang:element(3, erlang:element(3, Ctx)), Hooks = erlang:element(2, erlang:element(3, Ctx)), case sprocket@internal@utils@ordered_map:get(Hooks, Index) of {ok, Hook} -> {erlang:setelement( 3, Ctx, erlang:setelement(3, erlang:element(3, Ctx), Index + 1) ), Hook, Index}; {error, nil} -> case erlang:element(4, erlang:element(3, Ctx)) of true -> nil; false -> sprocket@internal@logger:error( <<<<" Hook not found for index: "/utf8, (gleam@int:to_string(Index))/binary>>/binary, ". This indicates a hook was dynamically created since first render which is not allowed. "/utf8>> ), erlang:error(#{gleam_error => panic, message => <<"panic expression evaluated"/utf8>>, module => <<"sprocket/context"/utf8>>, function => <<"fetch_or_init_hook"/utf8>>, line => 215}) end, Hook@1 = Init(), {erlang:setelement( 3, Ctx, {component_wip, sprocket@internal@utils@ordered_map:insert( erlang:element(2, erlang:element(3, Ctx)), Index, Hook@1 ), Index + 1, erlang:element(4, erlang:element(3, Ctx))} ), Hook@1, Index} end. -spec update_hook(context(), hook(), integer()) -> context(). update_hook(Ctx, Hook, Index) -> erlang:setelement( 3, Ctx, erlang:setelement( 2, erlang:element(3, Ctx), sprocket@internal@utils@ordered_map:update( erlang:element(2, erlang:element(3, Ctx)), Index, Hook ) ) ). -spec push_event_handler(context(), identifiable_handler()) -> {context(), sprocket@internal@utils@unique:unique()}. push_event_handler(Ctx, Identifiable_cb) -> {identifiable_handler, Id, Cb} = Identifiable_cb, {erlang:setelement( 4, Ctx, [{identifiable_handler, Id, Cb} | erlang:element(4, Ctx)] ), Id}. -spec get_event_handler(context(), sprocket@internal@utils@unique:unique()) -> {context(), {ok, identifiable_handler()} | {error, nil}}. get_event_handler(Ctx, Id) -> Handler = gleam@list:find( erlang:element(4, Ctx), fun(H) -> {identifiable_handler, I, _} = H, I =:= Id end ), {Ctx, Handler}. -spec dispatch_event( context(), sprocket@internal@utils@unique:unique(), binary(), gleam@option:option(binary()) ) -> {ok, nil} | {error, nil}. dispatch_event(Ctx, Id, Name, Payload) -> (erlang:element(7, Ctx))(Id, Name, Payload). -spec provider(binary(), any(), element()) -> element(). provider(Key, Value, Element) -> {provider, Key, gleam@dynamic:from(Value), Element}.