mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-21 15:41:00 +00:00
Add event dispatch system with more profile updates
This commit is contained in:
parent
5ca7357665
commit
e873619a66
11 changed files with 123 additions and 37 deletions
|
@ -1,9 +1,31 @@
|
|||
defmodule LiveBeats do
|
||||
@moduledoc """
|
||||
LiveBeats keeps the contexts that define your domain
|
||||
and business logic.
|
||||
require Logger
|
||||
|
||||
Contexts are also responsible for managing your data, regardless
|
||||
if it comes from the database, an external API or others.
|
||||
"""
|
||||
def attach(target_mod, opts) when is_atom(target_mod) do
|
||||
{src_mod, struct_mod} = Keyword.fetch!(opts, :to)
|
||||
_ = struct_mod.__struct__
|
||||
|
||||
:ok =
|
||||
:telemetry.attach(target_mod, [src_mod, struct_mod], &__MODULE__.handle_execute/4, %{
|
||||
target: target_mod
|
||||
})
|
||||
end
|
||||
|
||||
def execute(src_mod, event_struct) when is_struct(event_struct) do
|
||||
:telemetry.execute([src_mod, event_struct.__struct__], event_struct, %{})
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_execute([src_mod, event_mod], %event_mod{} = event_struct, _meta, %{target: target}) do
|
||||
try do
|
||||
target.handle_execute({src_mod, event_struct})
|
||||
catch
|
||||
kind, err ->
|
||||
Logger.error """
|
||||
executing {#{inspect(src_mod)}, #{inspect(event_mod)}} failed with #{inspect(kind)}
|
||||
|
||||
#{inspect(err)}
|
||||
"""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -70,9 +70,8 @@ defmodule LiveBeats.Accounts do
|
|||
set: [active_profile_user_id: profile_uid]
|
||||
)
|
||||
|
||||
Phoenix.PubSub.broadcast!(
|
||||
@pubsub,
|
||||
topic(current_user.id),
|
||||
broadcast!(
|
||||
current_user,
|
||||
%Events.ActiveProfileChanged{current_user: current_user, new_profile_user_id: profile_uid}
|
||||
)
|
||||
|
||||
|
@ -114,6 +113,14 @@ defmodule LiveBeats.Accounts do
|
|||
user
|
||||
|> change_settings(attrs)
|
||||
|> Repo.update()
|
||||
|> case do
|
||||
{:ok, new_user} ->
|
||||
LiveBeats.execute(__MODULE__, %Events.PublicSettingsChanged{user: new_user})
|
||||
{:ok, new_user}
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp update_github_token(%User{} = user, new_token) do
|
||||
|
@ -128,4 +135,8 @@ defmodule LiveBeats.Accounts do
|
|||
|
||||
{:ok, Repo.preload(user, :identities, force: true)}
|
||||
end
|
||||
|
||||
defp broadcast!(%User{} = user, msg) do
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(user.id), {__MODULE__, msg})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,4 +2,8 @@ defmodule LiveBeats.Accounts.Events do
|
|||
defmodule ActiveProfileChanged do
|
||||
defstruct current_user: nil, new_profile_user_id: nil
|
||||
end
|
||||
|
||||
defmodule PublicSettingsChanged do
|
||||
defstruct user: nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,8 +50,8 @@ defmodule LiveBeats.Accounts.User do
|
|||
|
||||
def settings_changeset(%User{} = user, params) do
|
||||
user
|
||||
|> cast(params, [:username])
|
||||
|> validate_required([:username])
|
||||
|> cast(params, [:username, :profile_tagline])
|
||||
|> validate_required([:username, :profile_tagline])
|
||||
|> validate_username()
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ defmodule LiveBeats.Application do
|
|||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
LiveBeats.MediaLibrary.attach()
|
||||
|
||||
children = [
|
||||
{Task.Supervisor, name: LiveBeats.TaskSupervisor},
|
||||
# Start the Ecto repository
|
||||
|
|
|
@ -16,6 +16,15 @@ defmodule LiveBeats.MediaLibrary do
|
|||
defdelegate playing?(song), to: Song
|
||||
defdelegate paused?(song), to: Song
|
||||
|
||||
def attach do
|
||||
LiveBeats.attach(LiveBeats.MediaLibrary, to: {Accounts, Accounts.Events.PublicSettingsChanged})
|
||||
end
|
||||
|
||||
def handle_execute({Accounts, %Accounts.Events.PublicSettingsChanged{user: user}}) do
|
||||
profile = get_profile!(user)
|
||||
broadcast!(user.id, %Events.PublicProfileUpdated{profile: profile})
|
||||
end
|
||||
|
||||
def subscribe_to_profile(%Profile{} = profile) do
|
||||
Phoenix.PubSub.subscribe(@pubsub, topic(profile.user_id))
|
||||
end
|
||||
|
@ -71,10 +80,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|
||||
elapsed = elapsed_playback(new_song)
|
||||
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(song.user_id), %Events.Play{
|
||||
song: song,
|
||||
elapsed: elapsed
|
||||
})
|
||||
broadcast!(song.user_id, %Events.Play{song: song, elapsed: elapsed})
|
||||
|
||||
new_song
|
||||
end
|
||||
|
@ -95,7 +101,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|> Multi.update_all(:now_paused, fn _ -> pause_query end, [])
|
||||
|> Repo.transaction()
|
||||
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(song.user_id), %Events.Pause{song: song})
|
||||
broadcast!(song.user_id, %Events.Pause{song: song})
|
||||
end
|
||||
|
||||
def play_next_song_auto(%Profile{} = profile) do
|
||||
|
@ -321,4 +327,8 @@ defmodule LiveBeats.MediaLibrary do
|
|||
defp order_by_playlist(%Ecto.Query{} = query, direction) when direction in [:asc, :desc] do
|
||||
from(s in query, order_by: [{^direction, s.inserted_at}, {^direction, s.id}])
|
||||
end
|
||||
|
||||
defp broadcast!(user_id, msg) when is_integer(user_id) do
|
||||
Phoenix.PubSub.broadcast!(@pubsub, topic(user_id), {__MODULE__, msg})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,4 +6,8 @@ defmodule LiveBeats.MediaLibrary.Events do
|
|||
defmodule Pause do
|
||||
defstruct song: nil
|
||||
end
|
||||
|
||||
defmodule PublicProfileUpdated do
|
||||
defstruct profile: nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -154,7 +154,9 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
when is_struct(profile, MediaLibrary.Profile) or is_nil(profile) do
|
||||
%{profile: prev_profile, current_user: current_user} = socket.assigns
|
||||
|
||||
if connected?(socket) do
|
||||
profile_changed? = profile_changed?(prev_profile, profile)
|
||||
|
||||
if connected?(socket) and profile_changed? do
|
||||
prev_profile && MediaLibrary.unsubscribe_to_profile(prev_profile)
|
||||
profile && MediaLibrary.subscribe_to_profile(profile)
|
||||
end
|
||||
|
@ -215,7 +217,11 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info(%Accounts.Events.ActiveProfileChanged{new_profile_user_id: user_id}, socket) do
|
||||
def handle_info(:play_current, socket) do
|
||||
{:noreply, play_current_song(socket)}
|
||||
end
|
||||
|
||||
def handle_info({Accounts, %Accounts.Events.ActiveProfileChanged{new_profile_user_id: user_id}}, socket) do
|
||||
if user_id do
|
||||
{:noreply, assign(socket, profile: get_profile(user_id))}
|
||||
else
|
||||
|
@ -223,18 +229,20 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_info(:play_current, socket) do
|
||||
{:noreply, play_current_song(socket)}
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.PublicProfileUpdated{} = update}, socket) do
|
||||
{:noreply, assign_profile(socket, update.profile)}
|
||||
end
|
||||
|
||||
def handle_info(%MediaLibrary.Events.Pause{}, socket) do
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.Pause{}}, socket) do
|
||||
{:noreply, push_pause(socket)}
|
||||
end
|
||||
|
||||
def handle_info(%MediaLibrary.Events.Play{song: song, elapsed: elapsed}, socket) do
|
||||
{:noreply, play_song(socket, song, elapsed)}
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.Play{} = play}, socket) do
|
||||
{:noreply, play_song(socket, play.song, play.elapsed)}
|
||||
end
|
||||
|
||||
def handle_info({MediaLibrary, _}, socket), do: {:noreply, socket}
|
||||
|
||||
defp play_song(socket, %Song{} = song, elapsed) do
|
||||
socket
|
||||
|> push_play(song, elapsed)
|
||||
|
@ -311,4 +319,11 @@ defmodule LiveBeatsWeb.PlayerLive do
|
|||
defp get_profile(user_id) do
|
||||
user_id && Accounts.get_user!(user_id) |> MediaLibrary.get_profile!()
|
||||
end
|
||||
|
||||
defp profile_changed?(nil = _prev_profile, nil = _new_profile), do: false
|
||||
defp profile_changed?(nil = _prev_profile, %MediaLibrary.Profile{}), do: true
|
||||
defp profile_changed?(%MediaLibrary.Profile{}, nil = _new_profile), do: true
|
||||
|
||||
defp profile_changed?(%MediaLibrary.Profile{} = prev, %MediaLibrary.Profile{} = new),
|
||||
do: prev.user_id != new.user_id
|
||||
end
|
||||
|
|
|
@ -125,18 +125,29 @@ defmodule LiveBeatsWeb.SongLive.Index do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info(%Accounts.Events.ActiveProfileChanged{new_profile_user_id: user_id}, socket) do
|
||||
{:noreply, assign(socket, active_profile_id: user_id)}
|
||||
def handle_info({Accounts, %Accounts.Events.ActiveProfileChanged{} = event}, socket) do
|
||||
{:noreply, assign(socket, active_profile_id: event.new_profile_user_id)}
|
||||
end
|
||||
|
||||
def handle_info(%MediaLibrary.Events.Play{song: song}, socket) do
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.PublicProfileUpdated{} = update}, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(profile: update.profile)
|
||||
|> push_patch(to: profile_path(update.profile))}
|
||||
end
|
||||
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.Play{song: song}}, socket) do
|
||||
{:noreply, play_song(socket, song)}
|
||||
end
|
||||
|
||||
def handle_info(%MediaLibrary.Events.Pause{song: song}, socket) do
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.Pause{song: song}}, socket) do
|
||||
{:noreply, pause_song(socket, song.id)}
|
||||
end
|
||||
|
||||
def handle_info({MediaLibrary, _}, socket), do: {:noreply, socket}
|
||||
|
||||
def handle_info({Accounts, _}, socket), do: {:noreply, socket}
|
||||
|
||||
defp stop_song(socket, song_id) do
|
||||
SongRowComponent.send_status(song_id, :stopped)
|
||||
|
||||
|
|
|
@ -22,10 +22,14 @@
|
|||
alt="Workflow">
|
||||
</div>
|
||||
<div class="mt-5 flex-1 h-0 overflow-y-auto">
|
||||
<.sidebar_account_dropdown id="mobile-account-dropdown" current_user={@current_user}/>
|
||||
<%= if @current_user do %>
|
||||
<.sidebar_account_dropdown id="mobile-account-dropdown" current_user={@current_user}/>
|
||||
<% end %>
|
||||
|
||||
<nav class="px-2">
|
||||
<.sidebar_nav_links current_user={@current_user}/>
|
||||
<%= if @current_user do %>
|
||||
<.sidebar_nav_links current_user={@current_user}/>
|
||||
<% end %>
|
||||
<.sidebar_active_users id="desktop-active-users" users={@active_users}/>
|
||||
</nav>
|
||||
</div>
|
||||
|
@ -46,7 +50,9 @@
|
|||
</div>
|
||||
<!-- Sidebar component, swap this element with another sidebar if you like -->
|
||||
<div class="h-0 flex-1 flex flex-col overflow-y-auto">
|
||||
<.sidebar_account_dropdown id="account-dropdown" current_user={@current_user}/>
|
||||
<%= if @current_user do %>
|
||||
<.sidebar_account_dropdown id="account-dropdown" current_user={@current_user}/>
|
||||
<% end %>
|
||||
<!-- Sidebar Search -->
|
||||
<div class="px-3 mt-5">
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
|
@ -66,7 +72,9 @@
|
|||
</div>
|
||||
<!-- Navigation -->
|
||||
<nav class="px-3 mt-6">
|
||||
<.sidebar_nav_links current_user={@current_user}/>
|
||||
<%= if @current_user do %>
|
||||
<.sidebar_nav_links current_user={@current_user}/>
|
||||
<% end %>
|
||||
<!-- Secondary navigation -->
|
||||
<.sidebar_active_users id="mobile-active-users" users={@active_users}/>
|
||||
</nav>
|
||||
|
|
|
@ -97,13 +97,12 @@ defmodule LiveBeatsWeb.LayoutView do
|
|||
redirect_to={profile_path(@current_user)}
|
||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
||||
>View Profile</.link>
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Settings</a>
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Notifications</a>
|
||||
</div>
|
||||
<div class="py-1" role="none">
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Get desktop app</a>
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Support</a>
|
||||
<.link
|
||||
redirect_to={Routes.settings_path(LiveBeatsWeb.Endpoint, :edit)}
|
||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem"
|
||||
>Settings</.link>
|
||||
</div>
|
||||
|
||||
<div class="py-1" role="none">
|
||||
<.link
|
||||
href={Routes.o_auth_callback_path(LiveBeatsWeb.Endpoint, :sign_out)}
|
||||
|
|
Loading…
Reference in a new issue