Skip to content

Commit

Permalink
Add Real-Time updates to Admin (#347)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkonidas committed Jul 23, 2023
1 parent b5e1cde commit 16187f7
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 25 deletions.
28 changes: 25 additions & 3 deletions lib/plexus/apps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ defmodule Plexus.Apps do
}) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def create_app(params) do
%App{}
|> change_app(params)
|> App.changeset(params)
|> Repo.insert()
|> broadcast(:app_created)
end

@spec update_app(App.t(), %{
Expand All @@ -66,8 +67,9 @@ defmodule Plexus.Apps do
}) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def update_app(%App{} = app, params) do
app
|> change_app(params)
|> App.changeset(params)
|> Repo.update()
|> broadcast(:app_updated)
end

@spec change_app(App.t(), map()) :: Ecto.Changeset.t()
Expand All @@ -77,7 +79,9 @@ defmodule Plexus.Apps do

@spec delete_app(App.t()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def delete_app(%App{} = app) do
Repo.delete(app)
app
|> Repo.delete()
|> broadcast(:app_deleted)
end

defp with_scores(query, opts) do
Expand Down Expand Up @@ -119,4 +123,22 @@ defmodule Plexus.Apps do
]
})
end

@spec subscribe :: :ok
def subscribe do
Phoenix.PubSub.subscribe(Plexus.PubSub, "apps")
end

@spec subscribe(String.t()) :: :ok
def subscribe(package) do
Phoenix.PubSub.subscribe(Plexus.PubSub, "apps:#{package}")
end

defp broadcast({:error, _reason} = error, _event), do: error

defp broadcast({:ok, app}, event) do
Phoenix.PubSub.broadcast!(Plexus.PubSub, "apps", {event, app})
Phoenix.PubSub.broadcast!(Plexus.PubSub, "apps:#{app.package}", {event, app})
{:ok, app}
end
end
9 changes: 9 additions & 0 deletions lib/plexus/ratings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,14 @@ defmodule Plexus.Ratings do
%Rating{}
|> Rating.changeset(params)
|> Repo.insert()
|> broadcast(:app_rating_updated)
end

defp broadcast({:error, _reason} = error, _event), do: error

defp broadcast({:ok, rating}, event) do
Phoenix.PubSub.broadcast!(Plexus.PubSub, "apps", {event, rating})
Phoenix.PubSub.broadcast!(Plexus.PubSub, "apps:#{rating.app_package}", {event, rating})
{:ok, rating}
end
end
9 changes: 6 additions & 3 deletions lib/plexus_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ defmodule PlexusWeb.CoreComponents do
attr :id, :string, default: "flash", doc: "the optional id of flash container"
attr :flash, :map, default: %{}, doc: "the map of flash messages to display"
attr :title, :string, default: nil
attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
attr :kind, :atom, values: [:info, :success, :error], doc: "used for styling and flash lookup"
attr :rest, :global, doc: "the arbitrary HTML attributes to add to the flash container"

slot :inner_block, doc: "the optional inner block that renders the flash message"
Expand All @@ -115,13 +115,15 @@ defmodule PlexusWeb.CoreComponents do
role="alert"
class={[
"fixed top-2 right-2 w-80 sm:w-96 z-50 rounded-lg p-3 ring-1",
@kind == :info && "bg-emerald-50 text-emerald-800 ring-emerald-500 fill-cyan-900",
@kind == :info && "bg-sky-50 text-sky-800 ring-sky-500 fill-cyan-900",
@kind == :success && "bg-emerald-50 text-emerald-800 ring-emerald-500 fill-cyan-900",
@kind == :error && "bg-rose-50 text-rose-900 shadow-md ring-rose-500 fill-rose-900"
]}
{@rest}
>
<p :if={@title} class="flex items-center gap-1.5 text-sm font-semibold leading-6">
<.icon :if={@kind == :info} name="hero-information-circle-mini" class="h-4 w-4" />
<.icon :if={@kind == :success} name="hero-information-circle-mini" class="h-4 w-4" />
<.icon :if={@kind == :error} name="hero-exclamation-circle-mini" class="h-4 w-4" />
<%= @title %>
</p>
Expand All @@ -144,7 +146,8 @@ defmodule PlexusWeb.CoreComponents do

def flash_group(assigns) do
~H"""
<.flash kind={:info} title="Success!" flash={@flash} />
<.flash kind={:info} title="Info" flash={@flash} />
<.flash kind={:success} title="Success!" flash={@flash} />
<.flash kind={:error} title="Error!" flash={@flash} />
<.flash
id="client-error"
Expand Down
20 changes: 4 additions & 16 deletions lib/plexus_web/live/admin/app_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,8 @@ defmodule PlexusWeb.Admin.AppLive.FormComponent do

defp save_app(socket, :edit, app_params) do
case Apps.update_app(socket.assigns.app, app_params) do
{:ok, app} ->
notify_parent({:saved, app})

{:noreply,
socket
|> put_flash(:info, "App updated successfully")
|> push_patch(to: socket.assigns.patch)}
{:ok, _app} ->
{:noreply, push_patch(socket, to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
Expand All @@ -70,13 +65,8 @@ defmodule PlexusWeb.Admin.AppLive.FormComponent do

defp save_app(socket, :new, app_params) do
case Apps.create_app(app_params) do
{:ok, app} ->
notify_parent({:saved, app})

{:noreply,
socket
|> put_flash(:info, "App created successfully")
|> push_patch(to: socket.assigns.patch)}
{:ok, _app} ->
{:noreply, push_patch(socket, to: socket.assigns.patch)}

{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, changeset)}
Expand All @@ -86,6 +76,4 @@ defmodule PlexusWeb.Admin.AppLive.FormComponent do
defp assign_form(socket, %Ecto.Changeset{} = changeset) do
assign(socket, :form, to_form(changeset))
end

defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
end
37 changes: 35 additions & 2 deletions lib/plexus_web/live/admin/app_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ defmodule PlexusWeb.Admin.AppLive.Index do

@impl Phoenix.LiveView
def mount(_params, _session, socket) do
if connected?(socket), do: Apps.subscribe()

{entries, page_metadata} =
[scores: true, order_by: :name, page_size: 9999]
|> Apps.list_apps()
Expand Down Expand Up @@ -42,9 +44,40 @@ defmodule PlexusWeb.Admin.AppLive.Index do
end

@impl Phoenix.LiveView
def handle_info({PlexusWeb.Admin.AppLive.FormComponent, {:saved, app}}, socket) do
def handle_info({:app_created, app}, socket) do
app = Apps.get_app!(app.package, scores: true)

{:noreply,
socket
|> update(:page_metadata, &%{&1 | total_entries: &1.total_entries + 1})
|> put_flash(:info, "'#{app.name}' Created")
|> stream_insert(:apps, app, at: 0)}
end

def handle_info({:app_updated, app}, socket) do
app = Apps.get_app!(app.package, scores: true)
{:noreply, stream_insert(socket, :apps, app, at: 0)}

{:noreply,
socket
|> put_flash(:info, "'#{app.name}' Updated")
|> stream_insert(:apps, app)}
end

def handle_info({:app_deleted, app}, socket) do
{:noreply,
socket
|> update(:page_metadata, &%{&1 | total_entries: &1.total_entries - 1})
|> put_flash(:info, "'#{app.name}' Deleted")
|> stream_delete(:apps, app)}
end

def handle_info({:app_rating_updated, rating}, socket) do
app = Apps.get_app!(rating.app_package, scores: true)

{:noreply,
socket
|> put_flash(:info, "'#{app.name}' Rating Updated")
|> stream_insert(:apps, app)}
end

@impl Phoenix.LiveView
Expand Down
26 changes: 25 additions & 1 deletion lib/plexus_web/live/admin/app_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ defmodule PlexusWeb.Admin.AppLive.Show do
alias Plexus.Apps

@impl Phoenix.LiveView
def mount(_params, _session, socket) do
def mount(%{"package" => package}, _session, socket) do
if connected?(socket), do: Apps.subscribe(package)
{:ok, socket}
end

Expand All @@ -16,6 +17,29 @@ defmodule PlexusWeb.Admin.AppLive.Show do
|> assign(:app, Apps.get_app!(package, scores: true))}
end

@impl Phoenix.LiveView
def handle_info({:app_updated, app}, socket) do
app = Apps.get_app!(app.package, scores: true)

{:noreply,
socket
|> put_flash(:info, "'#{app.name}' Updated")
|> assign(:app, app)}
end

def handle_info({:app_rating_updated, rating}, socket) do
app = Apps.get_app!(rating.app_package, scores: true)

{:noreply,
socket
|> put_flash(:info, "'#{app.name}' Rating Updated")
|> assign(:app, app)}
end

def handle_info({:app_deleted, _app}, socket) do
{:noreply, push_navigate(socket, to: ~p"/admin/apps")}
end

defp score(assigns) do
assigns =
assign(
Expand Down

0 comments on commit 16187f7

Please sign in to comment.