Skip to content

Commit

Permalink
feat: live updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ruioliveira02 committed Dec 22, 2024
1 parent 49cf096 commit edc5a21
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 19 deletions.
66 changes: 58 additions & 8 deletions lib/safira/minigames.ex
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ defmodule Safira.Minigames do
"""
def list_wheel_drops do
Repo.all(WheelDrop)
WheelDrop
|> order_by([wd], asc: wd.probability)
|> Repo.all()
|> Repo.preload([:badge, :prize])
end

@doc """
Expand Down Expand Up @@ -187,9 +190,13 @@ defmodule Safira.Minigames do
"""
def create_wheel_drop(attrs \\ %{}) do
%WheelDrop{}
|> WheelDrop.changeset(attrs)
|> Repo.insert()
result =
%WheelDrop{}
|> WheelDrop.changeset(attrs)
|> Repo.insert()

broadcast_wheel_config_update("drops", list_wheel_drops())
result
end

@doc """
Expand All @@ -205,9 +212,13 @@ defmodule Safira.Minigames do
"""
def update_wheel_drop(%WheelDrop{} = wheel_drop, attrs) do
wheel_drop
|> WheelDrop.changeset(attrs)
|> Repo.update()
result =
wheel_drop
|> WheelDrop.changeset(attrs)
|> Repo.update()

broadcast_wheel_config_update("drops", list_wheel_drops())
result
end

@doc """
Expand All @@ -223,7 +234,9 @@ defmodule Safira.Minigames do
"""
def delete_wheel_drop(%WheelDrop{} = wheel_drop) do
Repo.delete(wheel_drop)
result = Repo.delete(wheel_drop)
broadcast_wheel_config_update("drops", list_wheel_drops())
result
end

@doc """
Expand Down Expand Up @@ -325,10 +338,24 @@ defmodule Safira.Minigames do
|> Multi.merge(fn %{drop: drop, attendee: attendee} ->
add_spin_action(drop, attendee)
end)
|> Multi.run(:notify, fn _repo, params -> broadcast_spin_changes(params) end)
# Execute the transaction
|> Repo.transaction()
end

defp broadcast_spin_changes(params) do
case broadcast_wheel_win(Map.get(params, :spin)) do
:ok ->
case broadcast_wheel_config_update("drops", list_wheel_drops()) do
:ok -> {:ok, :ok}
e -> e
end

e ->
e
end
end

defp add_spin_action(drop, attendee) do
if is_nil(drop) or (is_nil(drop.badge_id) and is_nil(drop.prize_id)) do
# If there was no prize, or the prize was just tokens, don't insert it
Expand Down Expand Up @@ -530,6 +557,29 @@ defmodule Safira.Minigames do
Phoenix.PubSub.broadcast(@pubsub, wheel_config_topic(config), {config, value})
end

@doc """
Subscribes the caller to the wheel's wins.
## Examples
iex> subscribe_to_wheel_wins()
:ok
"""
def subscribe_to_wheel_wins() do

Check warning on line 568 in lib/safira/minigames.ex

View workflow job for this annotation

GitHub Actions / Code Quality (27.x, 1.17.x)

Do not use parentheses when defining a function which has no arguments.
Phoenix.PubSub.subscribe(@pubsub, "wheel_win")
end

defp broadcast_wheel_win(value) do
value = value |> Repo.preload(attendee: [:user], drop: [:prize, :badge])

if not is_nil(value) and not is_nil(value.drop) and
(not is_nil(value.drop.badge) or not is_nil(value.drop.prize)) do
Phoenix.PubSub.broadcast(@pubsub, "wheel_win", {"win", value})
else
:ok
end
end

# Generates a random number using the Erlang crypto module
defp strong_randomizer do
<<i1::unsigned-integer-32, i2::unsigned-integer-32, i3::unsigned-integer-32>> =
Expand Down
59 changes: 59 additions & 0 deletions lib/safira_web/live/app/wheel_live/components/awards.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule SafiraWeb.App.WheelLive.Components.Awards do
@moduledoc """
Lucky wheel awards component.
"""
use SafiraWeb, :component

attr :entries, :list, default: []

def awards(assigns) do
~H"""
<table class="w-full">
<tr class="border-b-2">
<th class="px-4 text-lg text-left">Name</th>
<th class="px-4 text-lg text-center">Stock</th>
<th class="px-4 text-lg text-center">Max. / Attendee</th>
<th class="px-4 text-lg text-right">Probability</th>
</tr>
<%= for entry <- @entries do %>
<tr>
<td class="px-4 py-2 font-bold text-left"><%= entry_name(entry) %></td>
<td class="px-4 py-2 font-bold text-center"><%= entry_stock(entry) %></td>
<td class="px-4 py-2 text-center"><%= entry.max_per_attendee %></td>
<td class="px-4 py-2 text-accent font-bold text-right">
<%= format_probability(entry.probability) %>
</td>
</tr>
<% end %>
</table>
"""
end

defp entry_stock(drop) do
if is_nil(drop.prize) do
"∞"
else
drop.prize.stock
end
end

defp format_probability(probability) do
"#{probability * 100} %"
end

defp entry_name(drop) do
cond do
not is_nil(drop.prize) ->
drop.prize.name

not is_nil(drop.badge) ->
drop.badge.name

drop.entries > 0 ->
"#{drop.entries} Entries"

drop.tokens > 0 ->
"#{drop.tokens} Tokens"
end
end
end
18 changes: 10 additions & 8 deletions lib/safira_web/live/app/wheel_live/components/latest_wins.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ defmodule SafiraWeb.App.WheelLive.Components.LatestWins do

def latest_wins(assigns) do
~H"""
<table class="m-auto">
<tr>
<th class="px-4">Attendee</th>
<th class="px-4">Prize</th>
<th class="px-4">When</th>
<table class="w-full">
<tr class="border-b-2">
<th class="px-4 text-lg text-left"><%= gettext("Attendee") %></th>
<th class="px-4 text-lg text-center"><%= gettext("Prize") %></th>
<th class="px-4 text-lg text-right"><%= gettext("When") %></th>
</tr>
<%= for entry <- @entries do %>
<tr>
<td class="px-4"><%= entry.attendee.user.name %></td>
<td class="px-4"><%= entry_name(entry) %></td>
<td class="px-4"><%= Timex.from_now(entry.inserted_at) %></td>
<td class="px-4 py-2 font-bold text-left"><%= entry.attendee.user.name %></td>
<td class="px-4 py-2 text-center"><%= entry_name(entry) %></td>
<td class="px-4 py-2 text-accent font-bold text-right">
<%= Timex.from_now(entry.inserted_at) %>
</td>
</tr>
<% end %>
</table>
Expand Down
25 changes: 24 additions & 1 deletion lib/safira_web/live/app/wheel_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ defmodule SafiraWeb.App.WheelLive.Index do
use SafiraWeb, :app_view

import SafiraWeb.App.WheelLive.Components.LatestWins
import SafiraWeb.App.WheelLive.Components.Awards
import SafiraWeb.App.WheelLive.Components.ResultModal
import SafiraWeb.App.WheelLive.Components.Wheel

alias Safira.Minigames

@max_wins 6

@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Minigames.subscribe_to_wheel_config_update("price")
Minigames.subscribe_to_wheel_config_update("is_active")
Minigames.subscribe_to_wheel_config_update("drops")
Minigames.subscribe_to_wheel_wins()
end

{:ok,
Expand All @@ -22,7 +27,8 @@ defmodule SafiraWeb.App.WheelLive.Index do
|> assign(:wheel_price, Minigames.get_wheel_price())
|> assign(:result, nil)
|> assign(:wheel_active?, Minigames.wheel_active?())
|> assign(:latest_wins, Minigames.wheel_latest_wins(5))}
|> assign(:latest_wins, Minigames.wheel_latest_wins(@max_wins))
|> assign(:drops, Minigames.list_wheel_drops())}
end

@impl true
Expand Down Expand Up @@ -84,6 +90,23 @@ defmodule SafiraWeb.App.WheelLive.Index do
{:noreply, socket |> assign(:wheel_active?, value)}
end

@impl true
def handle_info({"drops", value}, socket) do
{:noreply, socket |> assign(:drops, value)}
end

@impl true
def handle_info({"win", value}, socket) do
{:noreply,
socket
|> assign(:latest_wins, merge_wins(socket.assigns.latest_wins, value))}
end

defp merge_wins(latest_wins, new_win) do
([new_win] ++ latest_wins)
|> Enum.take(@max_wins)
end

defp can_spin?(wheel_active?, tokens, price, in_spin?) do
!in_spin? && wheel_active? && tokens >= price
end
Expand Down
15 changes: 13 additions & 2 deletions lib/safira_web/live/app/wheel_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
</span>
</:actions>
<div class="grid grid-cols-1 xl:grid-cols-2">
<div>
<div class="mt-12">
<div>
<h2 class="text-2xl font-terminal uppercase font-bold mb-8">
<%= gettext("Spin To Win!") %>
</h2>
</div>
<.wheel />
<div class="flex flex-row justify-center w-full pt-16">
<.action_button
Expand All @@ -18,8 +23,14 @@
</div>
</div>
<div class="mt-12">
<h2 class="text-xl font-semibold mb-8"><%= gettext("Latest Wins") %></h2>
<h2 class="text-2xl font-terminal uppercase font-bold mb-8">
<%= gettext("Latest Wins") %>
</h2>
<.latest_wins entries={@latest_wins} />
<h2 class="text-2xl font-terminal uppercase font-bold mt-24 mb-8">
<%= gettext("Awards") %>
</h2>
<.awards entries={@drops} />
</div>
</div>
</.page>
Expand Down

0 comments on commit edc5a21

Please sign in to comment.