Skip to content

Commit 3ae36fe

Browse files
feat: block routes based on event start time (#431)
1 parent 77e4d4e commit 3ae36fe

File tree

34 files changed

+516
-112
lines changed

34 files changed

+516
-112
lines changed

.env.dev.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ DB_HOST=localhost
44
DB_PORT=5432
55
DB_NAME=safira_dev
66
HOST_URL=http://localhost:4000
7-
ASSET_HOST=http://localhost:4000
7+
ASSET_HOST=http://localhost:4000

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ safira-*.tar
3737
npm-debug.log
3838
/assets/node_modules/
3939

40-
.env.dev
40+
.env.*
41+
.env

assets/js/app.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ import {Socket} from "phoenix"
2222
import {LiveSocket} from "phoenix_live_view"
2323
import topbar from "../vendor/topbar"
2424
import live_select from "live_select"
25-
import { QrScanner, Wheel, Confetti, Sorting } from "./hooks";
25+
import { QrScanner, Wheel, Confetti, Countdown, Sorting } from "./hooks";
2626

2727
let Hooks = {
2828
QrScanner: QrScanner,
2929
Wheel: Wheel,
3030
Confetti: Confetti,
31+
Countdown: Countdown,
3132
Sorting: Sorting,
3233
...live_select
3334
};

assets/js/hooks/countdown.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
export const Countdown = {
2+
mounted() {
3+
let countdownInterval = null;
4+
5+
const startCountdown = (startTime) => {
6+
if (countdownInterval) {
7+
clearInterval(countdownInterval);
8+
}
9+
10+
const textElement = document.getElementById("seconds-remaining");
11+
if (!textElement) {
12+
console.warn("Countdown element not found!");
13+
return;
14+
}
15+
16+
countdownInterval = setInterval(() => {
17+
const now = Date.now();
18+
const secondsLeft = Math.round((startTime - now) / 1000);
19+
20+
if (secondsLeft >= 0) {
21+
textElement.textContent = formatTime(secondsLeft);
22+
} else {
23+
clearInterval(countdownInterval);
24+
textElement.textContent = "00";
25+
window.location.reload();
26+
}
27+
}, 100);
28+
};
29+
30+
window.addEventListener("phx:highlight", (e) => {
31+
const startTime = new Date(e.detail.start_time).getTime();
32+
startCountdown(startTime);
33+
});
34+
}
35+
};
36+
37+
function formatTime(totalSeconds) {
38+
const dayToSeconds = 86400;
39+
const days = Math.floor(totalSeconds / dayToSeconds);
40+
const hours = Math.floor((totalSeconds % dayToSeconds) / 3600);
41+
const minutes = Math.floor((totalSeconds % 3600) / 60);
42+
const seconds = totalSeconds % 60;
43+
44+
const formattedTime = [
45+
days > 0 ? `${days} days` : null,
46+
`${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
47+
].filter(Boolean).join(", ");
48+
49+
return formattedTime;
50+
}

assets/js/hooks/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { QrScanner } from "./qr_reading.js";
22
export { Wheel } from "./wheel.js";
33
export { Confetti } from "./confetti.js";
4-
export { Sorting } from "./sorting.js";
4+
export { Sorting } from "./sorting.js";
5+
export { Countdown } from "./countdown.js";

lib/safira/accounts/roles/permissions.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule Safira.Accounts.Roles.Permissions do
1313
"purchases" => ["show", "redeem", "refund"],
1414
"badges" => ["show", "edit", "delete", "give", "revoke", "give_without_restrictions"],
1515
"minigames" => ["show", "edit", "simulate"],
16+
"event" => ["show", "edit"],
1617
"spotlights" => ["edit"],
1718
"schedule" => ["edit"],
1819
"statistics" => ["show"],

lib/safira/event.ex

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,75 @@ defmodule Safira.Event do
44
"""
55
alias Safira.Constants
66

7+
@pubsub Safira.PubSub
8+
9+
@doc """
10+
Returns whether the registrations for the event are open
11+
12+
## Examples
13+
14+
iex> registrations_open?()
15+
false
16+
"""
17+
def registrations_open? do
18+
case Constants.get("registrations_open") do
19+
{:ok, registrations_open} ->
20+
case String.downcase(registrations_open) do
21+
"true" -> true
22+
_ -> false
23+
end
24+
25+
_ ->
26+
false
27+
end
28+
end
29+
30+
def change_registrations_open(registrations_open) do
31+
Constants.set(
32+
"registrations_open",
33+
if registrations_open do
34+
"true"
35+
else
36+
"false"
37+
end
38+
)
39+
end
40+
41+
def get_event_start_time! do
42+
with {:ok, start_time_str} <- Constants.get("start_time") do
43+
with {:ok, start_time, _} <- DateTime.from_iso8601(start_time_str) do
44+
start_time
45+
end
46+
end
47+
end
48+
49+
def change_event_start_time(start_time) do
50+
result = Constants.set("start_time", DateTime.to_iso8601(start_time))
51+
broadcast_start_time_update("start_time", start_time)
52+
result
53+
end
54+
55+
@doc """
56+
Subscribes the caller to the start time's updates.
57+
58+
## Examples
59+
60+
iex> subscribe_to_start_time_update("start_time")
61+
:ok
62+
"""
63+
def subscribe_to_start_time_update(config) do
64+
Phoenix.PubSub.subscribe(@pubsub, config)
65+
end
66+
67+
defp broadcast_start_time_update(config, value) do
68+
Phoenix.PubSub.broadcast(@pubsub, "start_time", {config, value})
69+
end
70+
71+
def event_started? do
72+
start_time = get_event_start_time!()
73+
DateTime.compare(start_time, DateTime.utc_now()) == :lt
74+
end
75+
776
@doc """
877
Returns the event's start date.
978
If the date is not set, it will be set to today's date by default.

lib/safira_web.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ defmodule SafiraWeb do
4343
layouts: [html: SafiraWeb.Layouts]
4444

4545
import Plug.Conn
46-
import SafiraWeb.Gettext
46+
use Gettext, backend: SafiraWeb.Gettext
4747

4848
unquote(verified_routes())
4949
end
@@ -119,7 +119,7 @@ defmodule SafiraWeb do
119119
# Core UI components and translation
120120
import SafiraWeb.CoreComponents
121121
import SafiraWeb.Components.Page
122-
import SafiraWeb.Gettext
122+
use Gettext, backend: SafiraWeb.Gettext
123123

124124
import SafiraWeb.Helpers
125125

lib/safira_web/components/core_components.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ defmodule SafiraWeb.CoreComponents do
1919
alias Phoenix.HTML.Form
2020
alias Phoenix.LiveView.JS
2121

22-
import SafiraWeb.Gettext
22+
use Gettext, backend: SafiraWeb.Gettext
2323

2424
@doc """
2525
Renders a modal.

lib/safira_web/components/layouts/app.html.heex

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
<div class="relative h-screen flex overflow-hidden">
2-
<.sidebar
3-
current_user={@current_user}
4-
pages={SafiraWeb.Config.app_pages()}
5-
current_page={Map.get(assigns, :current_page, nil)}
6-
background="bg-primaryDark"
7-
border="border-darkShade"
8-
logo_padding="px-16 pt-8 pb-4"
9-
logo_images={%{light: "/images/sei.svg", dark: "/images/sei.svg"}}
10-
logo_url={~p"/app/"}
11-
user_dropdown_name_color="text-light"
12-
user_dropdown_handle_color="text-lightMuted"
13-
user_dropdown_icon_color="text-lightShade"
14-
link_class="px-3 group flex items-center py-2 text-sm font-medium rounded-md transition-colors"
15-
link_active_class="bg-light text-primaryDark"
16-
link_inactive_class="hover:bg-primary-500/10 text-light"
17-
/>
2+
<%= if Map.get(assigns, :event_started, true) do %>
3+
<.sidebar
4+
current_user={@current_user}
5+
pages={SafiraWeb.Config.app_pages()}
6+
current_page={Map.get(assigns, :current_page, nil)}
7+
background="bg-primaryDark"
8+
border="border-darkShade"
9+
logo_padding="px-16 pt-8 pb-4"
10+
logo_images={%{light: "/images/sei.svg", dark: "/images/sei.svg"}}
11+
logo_url={~p"/app/"}
12+
user_dropdown_name_color="text-light"
13+
user_dropdown_handle_color="text-lightMuted"
14+
user_dropdown_icon_color="text-lightShade"
15+
link_class="px-3 group flex items-center py-2 text-sm font-medium rounded-md transition-colors"
16+
link_active_class="bg-light text-primaryDark"
17+
link_inactive_class="hover:bg-primary-500/10 text-light"
18+
/>
19+
<% end %>
1820
<div class="flex flex-col flex-1 overflow-hidden">
1921
<div class="bg-primaryDark flex justify-end lg:hidden px-4 sm:px-6 py-2">
2022
<button

0 commit comments

Comments
 (0)