Skip to content

Commit 9245488

Browse files
committed
feat: rudimentary presence
1 parent cfdcf98 commit 9245488

File tree

11 files changed

+364
-24
lines changed

11 files changed

+364
-24
lines changed

valentine/assets/css/app.css

+21
Original file line numberDiff line numberDiff line change
@@ -479,4 +479,25 @@ ul {
479479
.presence-list li {
480480
display: inline;
481481
margin-right: 0.5rem;
482+
}
483+
484+
.SideNav-item .presence-list ul {
485+
list-style: none;
486+
padding: 0;
487+
margin: 0;
488+
overflow: hidden;
489+
}
490+
491+
.SideNav-item .presence-list li {
492+
font-size: 0;
493+
margin-right: 0.1rem;
494+
}
495+
496+
.SideNav-item .presence-list .Counter {
497+
border: 0px !important;
498+
border-radius: 0.25rem;
499+
font-size: .25rem;
500+
line-height: 0.5rem;
501+
min-width: 0.5rem;
502+
padding: 0;
482503
}

valentine/lib/valentine_web/components/layouts/app.html.heex

+93-15
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
</:item>
88
<:item is_full_width />
99
<:item>
10-
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render presence={
11-
@presence
12-
} />
10+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
11+
current_user={@current_user}
12+
presence={@presence}
13+
/>
1314
</:item>
1415
<:item>
1516
<.button
@@ -134,78 +135,155 @@
134135
<:item
135136
is_selected={@active_module == "Index" || @active_module == "Show"}
136137
navigate={"/workspaces/#{@workspace_id}"}
138+
class="d-flex flex-items-center flex-justify-between"
137139
>
138-
<.octicon name="tools-16" /> {gettext("Workspaces")}
140+
<span><.octicon name="tools-16" /> {gettext("Workspaces")}</span>
141+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
142+
current_user={@current_user}
143+
presence={@presence}
144+
active_module={["Index", "Show"]}
145+
workspace_id={@workspace_id}
146+
/>
139147
</:item>
140148
<:item
141149
:if={@workspace_id}
142150
is_selected={@active_module == "ApplicationInformation"}
143151
navigate={~p"/workspaces/#{@workspace_id}/application_information"}
152+
class="d-flex flex-items-center flex-justify-between"
144153
>
145-
<.octicon name="star-16" /> {gettext("Application Information")}
154+
<span><.octicon name="star-16" /> {gettext("Application Information")}</span>
155+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
156+
current_user={@current_user}
157+
presence={@presence}
158+
active_module="ApplicationInformation"
159+
workspace_id={@workspace_id}
160+
/>
146161
</:item>
147162
<:item
148163
:if={@workspace_id}
149164
is_selected={@active_module == "Architecture"}
150165
navigate={~p"/workspaces/#{@workspace_id}/architecture"}
166+
class="d-flex flex-items-center flex-justify-between"
151167
>
152-
<.octicon name="container-16" /> {gettext("Architecture")}
168+
<span><.octicon name="container-16" /> {gettext("Architecture")}</span>
169+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
170+
current_user={@current_user}
171+
presence={@presence}
172+
active_module="Architecture"
173+
workspace_id={@workspace_id}
174+
/>
153175
</:item>
154176
<:item
155177
:if={@workspace_id}
156178
is_selected={@active_module == "DataFlow"}
157179
navigate={~p"/workspaces/#{@workspace_id}/data_flow"}
180+
class="d-flex flex-items-center flex-justify-between"
158181
>
159-
<.octicon name="workflow-16" /> {gettext("Data Flow")}
182+
<span><.octicon name="workflow-16" /> {gettext("Data Flow")}</span>
183+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
184+
current_user={@current_user}
185+
presence={@presence}
186+
active_module="DataFlow"
187+
workspace_id={@workspace_id}
188+
/>
160189
</:item>
161190
<:item
162191
:if={@workspace_id}
163192
is_selected={@active_module == "Assumption"}
164193
navigate={~p"/workspaces/#{@workspace_id}/assumptions"}
194+
class="d-flex flex-items-center flex-justify-between"
165195
>
166-
<.octicon name="discussion-closed-16" /> {gettext("Assumptions")}
196+
<span><.octicon name="discussion-closed-16" /> {gettext("Assumptions")}</span>
197+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
198+
current_user={@current_user}
199+
presence={@presence}
200+
active_module="Assumption"
201+
workspace_id={@workspace_id}
202+
/>
167203
</:item>
168204
<:item
169205
:if={@workspace_id}
170206
is_selected={@active_module == "Threat"}
171207
navigate={~p"/workspaces/#{@workspace_id}/threats"}
208+
class="d-flex flex-items-center flex-justify-between"
172209
>
173-
<.octicon name="squirrel-16" /> {gettext("Threats")}
210+
<span><.octicon name="squirrel-16" /> {gettext("Threats")}</span>
211+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
212+
current_user={@current_user}
213+
presence={@presence}
214+
active_module="Threat"
215+
workspace_id={@workspace_id}
216+
/>
174217
</:item>
175218
<:item
176219
:if={@workspace_id}
177220
is_selected={@active_module == "Mitigation"}
178221
navigate={~p"/workspaces/#{@workspace_id}/mitigations"}
222+
class="d-flex flex-items-center flex-justify-between"
179223
>
180-
<.octicon name="check-circle-16" /> {gettext("Mitigations")}
224+
<span><.octicon name="check-circle-16" /> {gettext("Mitigations")}</span>
225+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
226+
current_user={@current_user}
227+
presence={@presence}
228+
active_module="Mitigation"
229+
workspace_id={@workspace_id}
230+
/>
181231
</:item>
182232
</.side_nav>
183233
<.side_nav :if={@workspace_id} aria_label="model" class="mt-4" is_border>
184234
<:item
185-
is_selected={@active_module == "Report"}
235+
is_selected={@active_module == "ThreatModel"}
186236
navigate={~p"/workspaces/#{@workspace_id}/threat_model"}
237+
class="d-flex flex-items-center flex-justify-between"
187238
>
188-
<.octicon name="file-badge-16" /> {gettext("Threat Model")}
239+
<span><.octicon name="file-badge-16" /> {gettext("Threat Model")}</span>
240+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
241+
current_user={@current_user}
242+
presence={@presence}
243+
active_module="ThreatModel"
244+
workspace_id={@workspace_id}
245+
/>
189246
</:item>
190247
</.side_nav>
191248
<.side_nav :if={@workspace_id} aria_label="reference-packs" class="mt-4" is_border>
192249
<:item
193250
is_selected={@active_module == "ReferencePacks"}
194251
navigate={~p"/workspaces/#{@workspace_id}/reference_packs"}
252+
class="d-flex flex-items-center flex-justify-between"
195253
>
196-
<.octicon name="repo-16" /> {gettext("Reference Packs")}
254+
<span><.octicon name="repo-16" /> {gettext("Reference Packs")}</span>
255+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
256+
current_user={@current_user}
257+
presence={@presence}
258+
active_module="ReferencePacks"
259+
workspace_id={@workspace_id}
260+
/>
197261
</:item>
198262
<:item
199263
is_selected={@active_module == "SRTM"}
200264
navigate={~p"/workspaces/#{@workspace_id}/srtm"}
265+
class="d-flex flex-items-center flex-justify-between"
201266
>
202-
<.octicon name="tasklist-16" /> {gettext("SRTM View")}
267+
<span><.octicon name="tasklist-16" /> {gettext("SRTM View")}</span>
268+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
269+
current_user={@current_user}
270+
presence={@presence}
271+
active_module="SRTM"
272+
workspace_id={@workspace_id}
273+
/>
203274
</:item>
204275
<:item
205276
is_selected={@active_module == "Controls"}
206277
navigate={~p"/workspaces/#{@workspace_id}/controls"}
278+
class="d-flex flex-items-center flex-justify-between"
207279
>
208-
<.octicon name="shield-lock-16" /> {gettext("NIST Controls")}
280+
<span><.octicon name="shield-lock-16" /> {gettext("NIST Controls")}</span>
281+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
282+
current_user={@current_user}
283+
presence={@presence}
284+
active_module="Controls"
285+
workspace_id={@workspace_id}
286+
/>
209287
</:item>
210288
</.side_nav>
211289
</:sidebar>

valentine/lib/valentine_web/helpers/auth_helper.ex

+1-3
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ defmodule ValentineWeb.Helpers.AuthHelper do
4848
end
4949

5050
defp generate_user_id() do
51-
:crypto.strong_rand_bytes(16)
52-
|> Base.encode64()
53-
|> String.trim_trailing("=")
51+
:crypto.strong_rand_bytes(8) |> Base.encode64() |> Kernel.<>("||") |> String.reverse()
5452
end
5553
end

valentine/lib/valentine_web/helpers/nav_helper.ex

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ defmodule ValentineWeb.Helpers.NavHelper do
99
end
1010

1111
defp set_active_module(params, _url, socket) do
12-
workspace_id = Map.get(params, "workspace_id", nil)
12+
workspace_id =
13+
case Map.get(params, "workspace_id") do
14+
nil -> Map.get(params, "id", nil)
15+
id -> id
16+
end
1317

1418
active_module =
1519
socket.view

valentine/lib/valentine_web/helpers/presence_helper.ex

+5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ defmodule ValentineWeb.Helpers.PresenceHelper do
1212
|> attach_hook(:presence_update, :handle_info, &handle_info/2)}
1313
end
1414

15+
# Sinkhole for presence_diff events as they are handled by the presence OTP process
1516
defp handle_info(%{event: "presence_diff"}, socket) do
17+
{:halt, socket}
18+
end
19+
20+
defp handle_info(%{event: "change"}, socket) do
1621
{:halt,
1722
socket
1823
|> Phoenix.Component.assign(:presence, Valentine.Cache.get("valentine:presence") || %{})}

valentine/lib/valentine_web/live/workspace_live/components/presence_indicator_component.ex

+45-3
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ defmodule ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent do
22
use Phoenix.Component
33
use PrimerLive
44

5+
attr :active_module, :any, default: nil
6+
attr :current_user, :string, default: ""
57
attr :presence, :map, default: %{}
8+
attr :workspace_id, :any, default: nil
69

710
def render(assigns) do
811
~H"""
912
<div class="presence-list">
1013
<ul>
11-
<%= for {{key, _presence}, index} <- @presence |> Enum.with_index() do %>
12-
<li title={key}>
13-
<.counter style={"color: #{get_colour(index)}; background-color: #{get_colour(index)}"}>
14+
<%= for {{key, %{metas: metas}}, index} <- @presence |> Enum.with_index() do %>
15+
<li :if={is_active(hd(metas), @active_module, @workspace_id)} title={get_name(key, index)}>
16+
<.counter style={"color: #{get_colour(index)}; background-color: #{get_colour(index)}; #{get_border(key, @current_user)}"}>
1417
{index}
1518
</.counter>
1619
</li>
@@ -20,7 +23,28 @@ defmodule ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent do
2023
"""
2124
end
2225

26+
defp is_active(_, nil, nil), do: true
27+
28+
defp is_active(%{workspace_id: workspace_id}, nil, workspace),
29+
do: workspace_id == workspace
30+
31+
defp is_active(%{module: module, workspace_id: workspace_id}, active_module, workspace)
32+
when is_list(active_module),
33+
do: module in active_module && workspace_id == workspace
34+
35+
defp is_active(%{module: module, workspace_id: workspace_id}, active_module, workspace),
36+
do: module == active_module && workspace_id == workspace
37+
38+
defp get_border(key, user_id) do
39+
if key == user_id do
40+
"border: 2px solid #fff;"
41+
else
42+
""
43+
end
44+
end
45+
2346
defp get_colour(index) when index > 8, do: get_colour(index - 9)
47+
2448
defp get_colour(index) do
2549
[
2650
"#2cbe4e",
@@ -35,4 +59,22 @@ defmodule ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent do
3559
]
3660
|> Enum.at(index)
3761
end
62+
63+
defp get_name("||" <> _key, index) do
64+
[
65+
"Anonymous Vancouverite",
66+
"Anonymous Ottawan",
67+
"Anonymous Haligonian",
68+
"Anonymous Yellowknifer",
69+
"Anonymous Calgarian",
70+
"Anonymous Winnipegger",
71+
"Anonymous Edmontonian",
72+
"Anonymous Yukoner",
73+
"Anonymous Montrealer",
74+
"Anonymous Banffite"
75+
]
76+
|> Enum.at(index)
77+
end
78+
79+
defp get_name(key, _index), do: key
3880
end

valentine/lib/valentine_web/live/workspace_live/components/workspace_component.ex

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ defmodule ValentineWeb.WorkspaceLive.Components.WorkspaceComponent do
3030
<.octicon name="trash-16" />
3131
</.button>
3232
</div>
33+
<div class="float-right mt-1">
34+
<ValentineWeb.WorkspaceLive.Components.PresenceIndicatorComponent.render
35+
current_user={@current_user}
36+
presence={@presence}
37+
active_module={nil}
38+
workspace_id={@workspace.id}
39+
/>
40+
</div>
3341
</div>
3442
</div>
3543
"""

valentine/lib/valentine_web/live/workspace_live/index.html.heex

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
<.live_component
4646
module={ValentineWeb.WorkspaceLive.Components.WorkspaceComponent}
4747
id={"workspace-#{workspace.id}"}
48+
current_user={@current_user}
49+
presence={@presence}
4850
workspace={workspace}
4951
/>
5052
</:row>

valentine/lib/valentine_web/presence.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ defmodule ValentineWeb.Presence do
44
pubsub_server: Valentine.PubSub
55

66
def init(_opts) do
7-
# user-land state
87
{:ok, %{}}
98
end
109

@@ -15,6 +14,7 @@ defmodule ValentineWeb.Presence do
1514
|> Map.drop(Map.keys(leaves))
1615

1716
Valentine.Cache.put("valentine:presence", presence)
17+
Phoenix.PubSub.broadcast(Valentine.PubSub, "valentine:presence", %{event: "change"})
1818
{:ok, state}
1919
end
2020
end

0 commit comments

Comments
 (0)