Current section

Files

Jump to
nitrogen_core src elements other element_sync_panel.erl
Raw

src/elements/other/element_sync_panel.erl

%% vim: ts=4 sw=4 et
-module(element_sync_panel).
-include("wf.hrl").
-export([
reflect/0,
render_element/1,
refresh/1,
refresh/2,
event/1,
update/3,
run_fun/1
]).
-define(STATE_KEY, sync_panel_body_trigger).
-spec reflect() -> [atom()].
reflect() -> record_info(fields, sync_panel).
-spec render_element(#sync_panel{}) -> #panel{}.
render_element(#sync_panel{body_fun=Fun})
when not(is_function(Fun))
andalso not(is_tuple(Fun) andalso (tuple_size(Fun)==2 orelse tuple_size(Fun)==3)) ->
throw({sync_panel_element, {body_fun_not_a_function, Fun}});
render_element(E = #sync_panel{
body_fun=BodyFun,
triggers=Triggers,
pool=Pool}) ->
Targetid = wf:temp_id(),
SyncPanel = E#sync_panel{id=Targetid},
start_comet(Targetid, Triggers, BodyFun, Pool),
register_target_body_fun(Targetid, BodyFun, Pool, Triggers),
Panel = wf_utils:copy_fields(SyncPanel, #panel{}),
Panel#panel{body=run_fun(BodyFun)}.
start_comet(Targetid, Triggers, BodyFun, Pool) ->
Reconnect = [#event{postback={reconnect, Targetid, Triggers, BodyFun, Pool}, delegate=?MODULE}],
CometFun = fun() -> update(Targetid, Triggers, BodyFun) end,
wf:wire(#comet{
pool=Pool,
scope=global,
function=CometFun,
reconnect_actions=Reconnect
}).
%wf:comet_global(fun() -> update(Targetid, Triggers, BodyFun) end, Pool).
run_fun(Fun) when is_function(Fun) ->
Fun();
run_fun({M,F}) ->
M:F();
run_fun({M,F,A}) ->
erlang:apply(M,F,A).
-spec register_target_body_fun(Targetid :: id(), BodyFun :: fun() | tuple(), Pool :: term(), [Trigger :: term()]) -> ok.
register_target_body_fun(_,_,_,[]) -> ok;
register_target_body_fun(Targetid, BodyFun, Pool, [Trigger|Triggers]) ->
wf:state({?STATE_KEY, Pool, Trigger},{Targetid, BodyFun}),
register_target_body_fun(Targetid, BodyFun, Pool, Triggers).
-spec get_target_body_fun(Pool :: term(), Trigger :: term()) -> {Targetid :: id(), BodyFun :: fun() | tuple()} | undefined.
get_target_body_fun(Pool, Trigger) ->
wf:state({?STATE_KEY, Pool, Trigger}).
-spec refresh(Trigger :: term()) -> ok.
refresh(Trigger) ->
refresh(sync_panel, Trigger).
-spec refresh(Pool :: term(), Trigger :: term()) -> ok.
refresh(Pool, Trigger) ->
case get_target_body_fun(Pool, Trigger) of
undefined ->
wf:send_global(Pool, {trigger, Trigger});
{Targetid, BodyFun} ->
Body = run_fun(BodyFun),%BodyFun(),
wf:update(Targetid, Body),
wf:send_global(Pool, {trigger, Trigger})
%% Disabled sending generated body across.
%% wf:send_global(Pool, {body, Trigger, Body})
end.
%% This is the private cycle that receives message
update(Targetid, Triggers, BodyFun) ->
receive
{trigger, T} ->
case lists:member(T, Triggers) of
true ->
Rendered = run_fun(BodyFun), %BodyFun(),
wf:update(Targetid, Rendered),
wf:flush();
false ->
do_nothing
end;
not_alive ->
exit(normal);
{'JOIN', _} ->
do_nothing;
{'LEAVE', _} ->
do_nothing
%% %% Sending Body to all clients?
%% %% Let's disable for now. Needs more thought.
%% {body, T, Body} ->
%% case lists:member(T, Triggers) of
%% true ->
%% wf:update(Targetid, Body);
%% false ->
%% do_nothing
%% end
after 20000 ->
%% The comet process will continue to live for as long as the comet pool exists.
{ok, TRef} = timer:send_after(20000, self(), not_alive),
wf:wire(#event{delegate=?MODULE, postback={still_alive, TRef}}),
wf:flush()
end,
?MODULE:update(Targetid, Triggers, BodyFun).
event({still_alive, TRef}) ->
timer:cancel(TRef);
event({reconnect, Targetid, Triggers, BodyFun, Pool}) ->
%?PRINT({reconnecting, Targetid, Triggers, BodyFun, Pool}),
Body = run_fun(BodyFun),
%?PRINT({updating, Targetid, Body}),
wf:update(Targetid, Body),
start_comet(Targetid, Triggers, BodyFun, Pool).