diff --git a/lib/ex_finance_web/live/public/cedears/cedears_live/show.ex b/lib/ex_finance_web/live/public/cedears/cedears_live/show.ex index 552ba98..a92d5a1 100644 --- a/lib/ex_finance_web/live/public/cedears/cedears_live/show.ex +++ b/lib/ex_finance_web/live/public/cedears/cedears_live/show.ex @@ -12,6 +12,10 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do @fetch_interval :timer.seconds(30) + # ---------------------------------------------------------------------------- + # Lifecycle + # + @impl true def mount(_params, _session, socket) do :ok = Currencies.subscribe_currencies() @@ -32,14 +36,21 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do |> assign_cedear_price_calc(cedear_price_calc) |> assign_stock_price(nil) |> assign_average_stock_price(Decimal.new(0)) - |> assign_stock_price_changes()} + |> assign_stock_price_changes() + |> assign_timeleft_to_next_update(nil) + |> assign_countdown_ref(nil)} end + # ---------------------------------------------------------------------------- + # Handlers + # + @impl true def handle_params(%{"id" => id}, _uri, socket) do cedear = Instruments.get_cedear!(id) - {:ok, stock_price_worker_pid, maybe_stock_price} = + {:ok, stock_price_worker_pid, + {maybe_stock_price, maybe_millis_to_next_update}} = StockPrices.subscribe_stock_price(cedear.symbol) socket = @@ -59,13 +70,15 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do end schedule_heartbeat(stock_price_worker_pid) + maybe_schedule_countdown(maybe_millis_to_next_update) {:noreply, socket |> assign_stock_price(maybe_stock_price) |> assign(:page_title, cedear.name) |> assign(:section_title, gettext("%{cedear} price", cedear: cedear.name)) - |> assign_cedear(cedear)} + |> assign_cedear(cedear) + |> assign_timeleft_to_next_update(maybe_millis_to_next_update)} end @impl true @@ -81,7 +94,8 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do @impl true def handle_info( - {:new_stock_price, %ExFinnhub.StockPrice{} = stock_price}, + {:new_stock_price, + {%ExFinnhub.StockPrice{} = stock_price, maybe_millis_to_next_update}}, socket ) do stock_price_changes = @@ -91,10 +105,13 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do stock_price ) + maybe_schedule_countdown(maybe_millis_to_next_update) + {:noreply, socket |> assign_stock_price(stock_price) - |> assign_stock_price_changes(stock_price_changes)} + |> assign_stock_price_changes(stock_price_changes) + |> assign_timeleft_to_next_update(maybe_millis_to_next_update)} end @impl true @@ -104,6 +121,37 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do {:noreply, socket} end + @impl true + def handle_info( + {:timeleft_to_next_update, 0}, + %{assigns: %{countdown_ref: countdown_ref}} = socket + ) do + if countdown_ref != nil do + Process.cancel_timer(countdown_ref) + end + + {:noreply, socket} + end + + def handle_info({:timeleft_to_next_update, nil}, socket), + do: {:noreply, socket} + + def handle_info( + {:timeleft_to_next_update, timeleft}, + %{assigns: %{countdown_ref: countdown_ref}} = socket + ) do + if countdown_ref != nil do + Process.cancel_timer(countdown_ref) + end + + countdown_ref = maybe_schedule_countdown(timeleft) + + {:noreply, + socket + |> assign_timeleft_to_next_update(timeleft) + |> assign_countdown_ref(countdown_ref)} + end + @impl true def handle_event( "change_cedear_price", @@ -134,6 +182,10 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do |> assign_form(changeset)} end + # ---------------------------------------------------------------------------- + # Assignment functions + # + defp assign_cedear(socket, cedear), do: assign(socket, :cedear, cedear) @@ -168,6 +220,18 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do defp assign_stock_price_changes(socket, stock_price_changes), do: assign(socket, :stock_price_changes, stock_price_changes) + defp assign_timeleft_to_next_update(socket, timeleft_to_next_update), + do: assign(socket, :timeleft_to_next_update, timeleft_to_next_update) + + @spec assign_countdown_ref(Phoenix.LiveView.Socket.t(), reference() | nil) :: + Phoenix.LiveView.Socket.t() + defp assign_countdown_ref(socket, countdown_ref), + do: assign(socket, :countdown_ref, countdown_ref) + + # ---------------------------------------------------------------------------- + # Render functions + # + defp render_country(%Cedear{country: country}), do: String.upcase(country) defp render_ratio(%Cedear{ratio: ratio}), do: "1:#{Decimal.round(ratio)}" @@ -215,6 +279,13 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do "#{fair_cedear_price}" end + @spec render_timeleft(number()) :: integer() + def render_timeleft(milliseconds), do: round(milliseconds / 1000) + + # ---------------------------------------------------------------------------- + # Colors functions + # + defp get_color_by_percentage_change(%{change_percentage: %Decimal{coef: 0}}), do: "gray" @@ -231,7 +302,25 @@ defmodule ExFinanceWeb.Public.CedearsLive.Show do defp get_color_by_price_change(%{change_price: %Decimal{sign: -1}}), do: "red" + # ---------------------------------------------------------------------------- + # Timers functions + # + @spec schedule_heartbeat(pid()) :: reference() defp schedule_heartbeat(pid), do: Process.send_after(self(), {:heartbeat, pid}, @fetch_interval) + + @spec maybe_schedule_countdown(non_neg_integer() | nil) :: reference() | nil + defp maybe_schedule_countdown(nil), do: nil + + defp maybe_schedule_countdown(millis) do + next_tick = :timer.seconds(1) + + _timeleft_ref = + Process.send_after( + self(), + {:timeleft_to_next_update, millis - next_tick}, + next_tick + ) + end end diff --git a/lib/ex_finance_web/live/public/cedears/cedears_live/show.html.heex b/lib/ex_finance_web/live/public/cedears/cedears_live/show.html.heex index ca31217..4c78626 100644 --- a/lib/ex_finance_web/live/public/cedears/cedears_live/show.html.heex +++ b/lib/ex_finance_web/live/public/cedears/cedears_live/show.html.heex @@ -51,22 +51,42 @@
-

- <%= gettext("Stock price") %> -

-

- USD <%= render_stock_price( - @stock_price_changes - ) %> -

-

- <%= gettext("Fair cedear price") %> -

-

- ARS <%= render_fair_cedear_price( - @stock_price_changes - ) %> -

+
+

+ <%= gettext("Stock price") %> +

+
+
+

+ + USD <%= render_stock_price(@stock_price_changes) %> + +

+
+
+ + <%= render_timeleft(@timeleft_to_next_update) %> + +
+
+
+
+

+ <%= gettext("Fair cedear price") %> +

+

+ + ARS <%= render_fair_cedear_price(@stock_price_changes) %> + +

+