Current section

Files

Jump to
sprocket src sprocket@hooks.erl
Raw

src/sprocket@hooks.erl

-module(sprocket@hooks).
-compile([no_auto_import, nowarn_unused_vars]).
-export([callback/4, client/4, effect/4, reducer/4, state/3]).
-export_type([state_or_dispatch_reducer/2]).
-type state_or_dispatch_reducer(NMR, NMS) :: shutdown |
{state_reducer, gleam@erlang@process:subject(NMR)} |
{dispatch_reducer, fun((NMR, NMS) -> NMR), NMS}.
-spec replace_callback(
sprocket@internal@identifiable_callback:callback_fn(),
gleam@option:option(list(gleam@dynamic:dynamic_()))
) -> sprocket@context:callback_result().
replace_callback(Callback_fn, Deps) ->
{callback_result, Callback_fn, Deps}.
-spec maybe_update_callback(
sprocket@internal@identifiable_callback:callback_fn(),
sprocket@context:hook_trigger(),
gleam@option:option(sprocket@context:callback_result())
) -> sprocket@context:callback_result().
maybe_update_callback(Callback_fn, Trigger, Prev) ->
case Trigger of
on_mount ->
replace_callback(Callback_fn, {some, []});
on_update ->
replace_callback(Callback_fn, none);
{with_deps, Deps} ->
case Prev of
{some,
{callback_result, _, {some, Prev_deps}} = Prev_callback_result} ->
case sprocket@context:compare_deps(Prev_deps, Deps) of
{changed, _} ->
replace_callback(Callback_fn, {some, Deps});
unchanged ->
Prev_callback_result
end;
{some, Prev_callback_result@1} ->
Prev_callback_result@1;
none ->
replace_callback(Callback_fn, {some, Deps});
_ ->
sprocket@internal@exceptions:throw_on_unexpected_hook_result(
{<<"handle_callback"/utf8>>, Prev}
)
end
end.
-spec callback(
sprocket@context:context(),
sprocket@internal@identifiable_callback:callback_fn(),
sprocket@context:hook_trigger(),
fun((sprocket@context:context(), sprocket@internal@identifiable_callback:identifiable_callback()) -> {NMV,
list(sprocket@context:element())})
) -> {NMV, list(sprocket@context:element())}.
callback(Ctx, Callback_fn, Trigger, Cb) ->
Init_callback = fun() ->
{callback,
sprocket@internal@utils@unique:cuid(erlang:element(8, Ctx)),
Callback_fn,
Trigger,
none}
end,
{Ctx@1, {callback, Id, _, _, Prev}, Index} = sprocket@context:fetch_or_init_hook(
Ctx,
Init_callback
),
Result = maybe_update_callback(Callback_fn, Trigger, Prev),
Ctx@2 = sprocket@context:update_hook(
Ctx@1,
{callback, Id, erlang:element(2, Result), Trigger, {some, Result}},
Index
),
Cb(Ctx@2, {identifiable_callback, Id, erlang:element(2, Result)}).
-spec client(
sprocket@context:context(),
binary(),
gleam@option:option(fun((binary(), gleam@option:option(gleam@dynamic:dynamic_()), fun((binary(), gleam@option:option(binary())) -> {ok,
nil} |
{error, nil})) -> nil)),
fun((sprocket@context:context(), fun(() -> sprocket@html@attributes:attribute()), fun((binary(), gleam@option:option(binary())) -> {ok,
nil} |
{error, nil})) -> {sprocket@context:context(),
list(sprocket@context:element())})
) -> {sprocket@context:context(), list(sprocket@context:element())}.
client(Ctx, Name, Handle_event, Cb) ->
Init = fun() ->
{client,
sprocket@internal@utils@unique:cuid(erlang:element(8, Ctx)),
Name,
Handle_event}
end,
{Ctx@1, {client, Id, _, _}, Index} = sprocket@context:fetch_or_init_hook(
Ctx,
Init
),
Ctx@2 = sprocket@context:update_hook(
Ctx@1,
{client, Id, Name, Handle_event},
Index
),
Bind_hook_attr = fun() -> sprocket@html@attributes:client_hook(Id, Name) end,
Dispatch_event = fun(Name@1, Payload) ->
sprocket@context:dispatch_event(Ctx@2, Id, Name@1, Payload)
end,
Cb(Ctx@2, Bind_hook_attr, Dispatch_event).
-spec effect(
sprocket@context:context(),
fun(() -> gleam@option:option(fun(() -> nil))),
sprocket@context:hook_trigger(),
fun((sprocket@context:context()) -> {sprocket@context:context(),
list(sprocket@context:element())})
) -> {sprocket@context:context(), list(sprocket@context:element())}.
effect(Ctx, Effect_fn, Trigger, Cb) ->
Init = fun() ->
{effect,
sprocket@internal@utils@unique:cuid(erlang:element(8, Ctx)),
Effect_fn,
Trigger,
none}
end,
{Ctx@1, {effect, Id, _, _, Prev}, Index} = sprocket@context:fetch_or_init_hook(
Ctx,
Init
),
Ctx@2 = sprocket@context:update_hook(
Ctx@1,
{effect, Id, Effect_fn, Trigger, Prev},
Index
),
Cb(Ctx@2).
-spec reducer(
sprocket@context:context(),
NNF,
fun((NNF, NNG) -> NNF),
fun((sprocket@context:context(), NNF, fun((NNG) -> nil)) -> {sprocket@context:context(),
list(sprocket@context:element())})
) -> {sprocket@context:context(), list(sprocket@context:element())}.
reducer(Ctx, Initial, Reducer, Cb) ->
{context, _, _, _, Render_update, _, _, _} = Ctx,
Reducer_init = fun() ->
_assert_subject = gleam@otp@actor:start(
Initial,
fun(Message, State) -> case Message of
shutdown ->
{stop, normal};
{state_reducer, Reply_with} ->
gleam@erlang@process:send(Reply_with, State),
gleam@otp@actor:continue(State);
{dispatch_reducer, R, M} ->
_pipe = R(State, M),
gleam@otp@actor:continue(_pipe)
end end
),
{ok, Reducer_actor} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"sprocket/hooks"/utf8>>,
function => <<"reducer"/utf8>>,
line => 181})
end,
{reducer,
sprocket@internal@utils@unique:cuid(erlang:element(8, Ctx)),
gleam@dynamic:from(Reducer_actor),
fun() -> gleam@erlang@process:send(Reducer_actor, shutdown) end}
end,
_assert_subject@1 = sprocket@context:fetch_or_init_hook(Ctx, Reducer_init),
{Ctx@1, {reducer, _, Dyn_reducer_actor, _}, _} = case _assert_subject@1 of
{_, {reducer, _, _, _}, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"sprocket/hooks"/utf8>>,
function => <<"reducer"/utf8>>,
line => 211})
end,
Reducer_actor@1 = gleam@dynamic:unsafe_coerce(Dyn_reducer_actor),
State@1 = gleam@erlang@process:call(
Reducer_actor@1,
fun(_capture) -> {state_reducer, _capture} end,
sprocket@internal@constants:call_timeout()
),
Dispatch = fun(Msg) ->
gleam@otp@actor:send(Reducer_actor@1, {dispatch_reducer, Reducer, Msg}),
Render_update()
end,
Cb(Ctx@1, State@1, Dispatch).
-spec state(
sprocket@context:context(),
NNL,
fun((sprocket@context:context(), NNL, fun((NNL) -> nil)) -> {sprocket@context:context(),
list(sprocket@context:element())})
) -> {sprocket@context:context(), list(sprocket@context:element())}.
state(Ctx, Initial, Cb) ->
{context, _, _, _, Render_update, Update_hook, _, _} = Ctx,
Init_state = fun() ->
{state,
sprocket@internal@utils@unique:cuid(erlang:element(8, Ctx)),
gleam@dynamic:from(Initial)}
end,
_assert_subject = sprocket@context:fetch_or_init_hook(Ctx, Init_state),
{Ctx@1, {state, Hook_id, Value}, _} = case _assert_subject of
{_, {state, _, _}, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"sprocket/hooks"/utf8>>,
function => <<"state"/utf8>>,
line => 247})
end,
Setter = fun(Value@1) ->
Update_hook(Hook_id, fun(Hook) -> case Hook of
{state, Id, _} when Id =:= Hook_id ->
{state, Id, gleam@dynamic:from(Value@1)};
_ ->
sprocket@internal@exceptions:throw_on_unexpected_hook_result(
Hook
)
end end),
Render_update()
end,
Cb(Ctx@1, gleam@dynamic:unsafe_coerce(Value), Setter).