n4u_nitrogen.erl 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. -module(n4u_nitrogen).
  2. -include_lib("n4u/include/n4u.hrl").
  3. -export([info/3]).
  4. -export([render_actions/1, html_events/2, render_ev/4, receive_actions/1]).
  5. % todo compare here and in nitro - render_actions, html_events, render_ev, receive_actions
  6. % nitrogen pickle handler
  7. info({init, Rest}, Req, State) ->
  8. Module = State#cx.module,
  9. InitActionsReply = case Rest of
  10. <<>> ->
  11. try Elements = Module:main(),
  12. nitro:render(Elements),
  13. {ok, []}
  14. catch E1:R1:Stk1 ->
  15. wf:error(?MODULE, "main: E:R:Stk: ~p ~p~n ~p~n", [E1, R1, Stk1]),
  16. {error, [E1, R1, Stk1]}
  17. end;
  18. Binary ->
  19. Pid = wf:depickle(Binary),
  20. Pid ! {'N4U', erlang:self()},
  21. {ok, receive_actions(Req)}
  22. end,
  23. case InitActionsReply of
  24. {ok, InitActions} ->
  25. UserCx = try Module:event(init),
  26. case application:get_env(n4u, auto_session, "") of
  27. disabled -> skip;
  28. _ -> n4u_session:ensure_sid([], ?CTX, [])
  29. end
  30. catch E2:R2:Stk2 ->
  31. wf:error(?MODULE, "init: E:R:Stk: ~p ~p~n ~p~n", [E2, R2, Stk2]),
  32. {stack, [E2, R2, Stk2]}
  33. end,
  34. Actions = render_actions(erlang:get(actions)),
  35. {reply, wf:format({io, erlang:iolist_to_binary([InitActions, Actions]), <<>>}),
  36. Req, n4u_cx:context(State, ?MODULE, {init, UserCx})};
  37. {error, E} ->
  38. {reply, wf:format({io, <<>>, E}), Req,
  39. n4u_cx:context(State, ?MODULE, {error, E})}
  40. end;
  41. info({pickle, _, _, _} = Event, Req, State) ->
  42. erlang:put(actions, []),
  43. Result = try html_events(Event, State)
  44. catch E:R:Stk ->
  45. wf:error(?MODULE, "info pickle E:R:Stk: ~p ~p~n ~p~n", [E, R, Stk]),
  46. {io, render_actions(erlang:get(actions)), [E, R, Stk]}
  47. end,
  48. {reply, wf:format(Result), Req,
  49. n4u_cx:context(State, ?MODULE, {pickle, Result})};
  50. info({flush, Actions}, Req, State) ->
  51. erlang:put(actions, []),
  52. Render = erlang:iolist_to_binary(render_actions(Actions)),
  53. wf:info(?MODULE, "Flush Message: ~tp~n", [Render]),
  54. {reply, wf:format({io, Render, <<>>}), Req, State};
  55. info({direct, Message}, Req, State) ->
  56. erlang:put(actions, []),
  57. Module = State#cx.module,
  58. Result = try Res = Module:event(Message),
  59. {direct, Res}
  60. catch E:R:Stk ->
  61. wf:error(?MODULE, "info direct E:R:Stk: ~p ~p~n ~p~n", [E, R, Stk]),
  62. {stack, [E, R, Stk]}
  63. end,
  64. {reply, wf:format({io, render_actions(erlang:get(actions)), <<>>}),
  65. Req, n4u_cx:context(State, ?MODULE, Result)};
  66. info(Message, Req, State) -> {unknown, Message, Req, State}.
  67. % double render: actions could generate actions
  68. render_actions(Actions) ->
  69. erlang:put(actions, []),
  70. First = nitro:render(Actions),
  71. Second = nitro:render(erlang:get(actions)),
  72. erlang:put(actions, []),
  73. [First, Second].
  74. % N4U events
  75. html_events({pickle, Source, Pickled, Linked} = Pickle, State) ->
  76. wf:info(?MODULE, "Pickle: ~tp~n", [Pickle]),
  77. Ev = wf:depickle(Pickled),
  78. _Result = case Ev of
  79. #ev{} ->
  80. render_ev(Ev, Source, Linked, State);
  81. Custom_Envelop ->
  82. wf:error("Only #ev{} events for now: ~p~n", [Custom_Envelop])
  83. end,
  84. {io, render_actions(erlang:get(actions)), <<>>}.
  85. render_ev(#ev{module = M, name = F, msg = P, trigger = T}, _Source, Linked, State) ->
  86. case F of
  87. api_event ->
  88. M:F(P, Linked, State);
  89. event ->
  90. lists:map(fun({K, V}) ->
  91. erlang:put(K, nitro:to_binary(V))
  92. end, Linked),
  93. M:F(P);
  94. _UserCustomEvent ->
  95. M:F(P, T, State)
  96. end.
  97. receive_actions(Req) ->
  98. receive
  99. {actions, A} ->
  100. n4u_nitrogen:render_actions(A);
  101. _ ->
  102. receive_actions(Req)
  103. after 200 ->
  104. #{qs := QS} = Req,
  105. R = case QS of
  106. <<"">> -> "";
  107. _ -> "?" ++ nitro:to_list(QS)
  108. end,
  109. wf:redirect(R),
  110. []
  111. end.