forked from sunneach/SlideBlast
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathattendee_list_element.erl
124 lines (102 loc) · 4.36 KB
/
attendee_list_element.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
-module (attendee_list_element).
-include_lib ("nitrogen/include/wf.hrl").
-include ("caster.hrl").
-compile(export_all).
-record (attendee, {name, pid, last_update}).
-define (INACTIVE_AFTER, 30 * 1000 * 1000). % In MICROSECONDS
%% Custom element to show a list of all attendees viewing this slide deck.
%% Show active attendees in black, and inactive attendees in gray.
%% An inactive attendee is an attendee whose web browser hasn't sent a "tick"
%% message in about 30 seconds.
% Required for a custom element.
reflect() -> record_info(fields, attendee_list).
% Executes when the element is rendered.
render_element(Record) ->
DeckID = Record#attendee_list.deck_id,
{ok, Pid} = wf:comet_global(fun() -> comet_loop(DeckID) end, DeckID),
view:server_put(attendee_comet_pid, Pid),
[
#span { class=attendee_title, text="Attendees" },
#panel { id=attendeeList }
].
% Show the list of all connected attendees.
print_attendee_list(L) ->
[begin
#panel { id=attendee_id(X), class=attendee, body=X#attendee.name }
end || X <- L].
% Add CSS classes to indicate the status of attendees.
% Add the 'selected' class to the current attendee.
% Add an 'old' class to any attendees who haven't checked in for a while.
color_attendee_list(L, LastTick) ->
% Select the
wf:wire(attendee_id(self()), #add_class { class=selected }),
[begin
IsOld = timer:now_diff(LastTick, X#attendee.last_update) > ?INACTIVE_AFTER,
case IsOld of
true -> wf:wire(attendee_id(X), #add_class { class="old" });
false -> ignore
end
end || X <- L].
%%% EVENTS %%%
action() ->
DeckID = view:server_get(deck_id),
case view:server_get(attendee_comet_pid) of
undefined -> ignore;
Pid -> wf:send_global(DeckID, {tick, Pid})
end.
%%% COMET %%%
comet_loop(DeckID) ->
view:server_put(attendee_comet_pid, self()),
Me = #attendee { name="Joining...", pid=self(), last_update=now() },
wf:send_global(DeckID, {hello, Me}),
comet_loop([Me], now()).
comet_loop(L, LastTick) ->
receive
{'JOIN', Pid} -> % Sent when any comet processes join.
Pid!{latest_attendee_list, L},
comet_loop(L, LastTick);
{latest_attendee_list, NewL} when L /= NewL ->
% We've been given a new attendee list. Update the web page.
wf:update(attendeeList, print_attendee_list(NewL)),
color_attendee_list(NewL, LastTick),
wf:flush(),
comet_loop(NewL, LastTick);
{set_name, Name} ->
% We've change our name. Broadcast to the pool..
Me = lists:keyfind(self(), 3, L),
NewMe = Me#attendee { name=Name },
NewL = lists:sort(lists:keystore(self(), 3, L, NewMe)),
DeckID = view:server_get(deck_id),
wf:send_global(DeckID, {hello, NewMe}),
comet_loop(NewL, LastTick);
{hello, A} ->
% Someone has changed their name. Update our attendee list...
NewL = lists:sort(lists:keystore(A#attendee.pid, 3, L, A)),
wf:update(attendeeList, print_attendee_list(NewL)),
color_attendee_list(NewL, LastTick),
wf:flush(),
comet_loop(NewL, LastTick);
{tick, Pid} ->
% An attendee pid has sent a keepalive message.
% Recolor the attendee list.
A = lists:keyfind(Pid, 3, L),
NewL = lists:keystore(Pid, 3, L, A#attendee { last_update=now() }),
NewLastTick = now(),
color_attendee_list(NewL, NewLastTick),
wf:flush(),
comet_loop(NewL, NewLastTick);
{'LEAVE', Pid} ->
% An attendee has departed. Update and recolor the attendee list.
NewL = lists:keydelete(Pid, 3, L),
wf:update(attendeeList, print_attendee_list(NewL)),
color_attendee_list(NewL, LastTick),
wf:flush(),
comet_loop(NewL, LastTick);
_Other ->
comet_loop(L, LastTick)
end.
attendee_id(A) when is_record(A, attendee) -> attendee_id(A#attendee.pid);
attendee_id(Pid) when is_pid(Pid) -> "a" ++ clean(pid_to_list(Pid)).
clean([]) -> [];
clean([H|T]) when H >= $0 andalso H =< $9 -> [H|clean(T)];
clean([_|T]) -> clean(T).