-module(n4u_nitrogen). -include_lib("n4u/include/n4u.hrl"). -export([info/3]). -export([render_actions/1, html_events/2, render_ev/4, receive_actions/1]). % todo compare here and in nitro - render_actions, html_events, render_ev, receive_actions % nitrogen pickle handler info({init, Rest}, Req, State) -> Module = State#cx.module, InitActionsReply = case Rest of <<>> -> try Elements = Module:main(), nitro:render(Elements), {ok, []} catch E1:R1:Stk1 -> wf:error(?MODULE, "main: E:R:Stk: ~p ~p~n ~p~n", [E1, R1, Stk1]), {error, [E1, R1, Stk1]} end; Binary -> Pid = wf:depickle(Binary), Pid ! {'N4U', erlang:self()}, {ok, receive_actions(Req)} end, case InitActionsReply of {ok, InitActions} -> UserCx = try Module:event(init), case application:get_env(n4u, auto_session, "") of disabled -> skip; _ -> n4u_session:ensure_sid([], ?CTX, []) end catch E2:R2:Stk2 -> wf:error(?MODULE, "init: E:R:Stk: ~p ~p~n ~p~n", [E2, R2, Stk2]), {stack, [E2, R2, Stk2]} end, Actions = render_actions(erlang:get(actions)), {reply, wf:format({io, erlang:iolist_to_binary([InitActions, Actions]), <<>>}), Req, n4u_cx:context(State, ?MODULE, {init, UserCx})}; {error, E} -> {reply, wf:format({io, <<>>, E}), Req, n4u_cx:context(State, ?MODULE, {error, E})} end; info({pickle, _, _, _} = Event, Req, State) -> erlang:put(actions, []), Result = try html_events(Event, State) catch E:R:Stk -> wf:error(?MODULE, "info pickle E:R:Stk: ~p ~p~n ~p~n", [E, R, Stk]), {io, render_actions(erlang:get(actions)), [E, R, Stk]} end, {reply, wf:format(Result), Req, n4u_cx:context(State, ?MODULE, {pickle, Result})}; info({flush, Actions}, Req, State) -> erlang:put(actions, []), Render = erlang:iolist_to_binary(render_actions(Actions)), wf:info(?MODULE, "Flush Message: ~tp~n", [Render]), {reply, wf:format({io, Render, <<>>}), Req, State}; info({direct, Message}, Req, State) -> erlang:put(actions, []), Module = State#cx.module, Result = try Res = Module:event(Message), {direct, Res} catch E:R:Stk -> wf:error(?MODULE, "info direct E:R:Stk: ~p ~p~n ~p~n", [E, R, Stk]), {stack, [E, R, Stk]} end, {reply, wf:format({io, render_actions(erlang:get(actions)), <<>>}), Req, n4u_cx:context(State, ?MODULE, Result)}; info(Message, Req, State) -> {unknown, Message, Req, State}. % double render: actions could generate actions render_actions(Actions) -> erlang:put(actions, []), First = nitro:render(Actions), Second = nitro:render(erlang:get(actions)), erlang:put(actions, []), [First, Second]. % N4U events html_events({pickle, Source, Pickled, Linked} = Pickle, State) -> wf:info(?MODULE, "Pickle: ~tp~n", [Pickle]), Ev = wf:depickle(Pickled), _Result = case Ev of #ev{} -> render_ev(Ev, Source, Linked, State); Custom_Envelop -> wf:error("Only #ev{} events for now: ~p~n", [Custom_Envelop]) end, {io, render_actions(erlang:get(actions)), <<>>}. render_ev(#ev{module = M, name = F, msg = P, trigger = T}, _Source, Linked, State) -> case F of api_event -> M:F(P, Linked, State); event -> lists:map(fun({K, V}) -> erlang:put(K, nitro:to_binary(V)) end, Linked), M:F(P); _UserCustomEvent -> M:F(P, T, State) end. receive_actions(Req) -> receive {actions, A} -> n4u_nitrogen:render_actions(A); _ -> receive_actions(Req) after 200 -> #{qs := QS} = Req, R = case QS of <<"">> -> ""; _ -> "?" ++ nitro:to_list(QS) end, wf:redirect(R), [] end.