mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-21 15:41:00 +00:00
Optimize presence.
Avoid fetching each user by passing in full pre-fetched presences from Presence.fetch/2 callback. Use temporary assigns in ProfileLive to avoid duping presences in memeory. Handle removes by a small hook event
This commit is contained in:
parent
a65c789748
commit
9998e06caa
6 changed files with 56 additions and 41 deletions
|
@ -195,6 +195,7 @@ window.addEventListener("phx:page-loading-stop", info => topbar.hide())
|
|||
window.addEventListener("phx:page-loading-stop", routeUpdated)
|
||||
|
||||
window.addEventListener("js:exec", e => e.target[e.detail.call](...e.detail.args))
|
||||
window.addEventListener("phx:remove-el", e => document.getElementById(e.detail.id).remove())
|
||||
|
||||
// connect if there are any LiveViews on the page
|
||||
liveSocket.getSocket().onOpen(() => execJS("#connection-status", "js-hide"))
|
||||
|
|
|
@ -2,8 +2,10 @@ defmodule Phoenix.Presence.Client do
|
|||
use GenServer
|
||||
|
||||
@callback init(state :: term) :: {:ok, new_state :: term}
|
||||
@callback handle_join(topic :: String.t(), key :: String.t(), meta :: [map()], state :: term) :: {:ok, term}
|
||||
@callback handle_leave(topic :: String.t(), key :: String.t(), meta :: [map()], state :: term) :: {:ok, term}
|
||||
@callback handle_join(topic :: String.t(), key :: String.t(), meta :: [map()], state :: term) ::
|
||||
{:ok, term}
|
||||
@callback handle_leave(topic :: String.t(), key :: String.t(), meta :: [map()], state :: term) ::
|
||||
{:ok, term}
|
||||
|
||||
@doc """
|
||||
TODO
|
||||
|
@ -19,7 +21,8 @@ defmodule Phoenix.Presence.Client do
|
|||
{:ok, name} ->
|
||||
GenServer.start_link(__MODULE__, opts, name: name)
|
||||
|
||||
:error -> GenServer.start_link(__MODULE__, opts)
|
||||
:error ->
|
||||
GenServer.start_link(__MODULE__, opts)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -113,7 +116,9 @@ defmodule Phoenix.Presence.Client do
|
|||
updated_state =
|
||||
update_topics_state(:add_new_presence_or_metas, state, topic, joined_key, joined_meta)
|
||||
|
||||
{:ok, updated_client_state} = state.client.handle_join(topic, joined_key, joined_meta, state.client_state)
|
||||
{:ok, updated_client_state} =
|
||||
state.client.handle_join(topic, joined_key, meta, state.client_state)
|
||||
|
||||
updated_state = Map.put(updated_state, :client_state, updated_client_state)
|
||||
|
||||
{updated_state, topic}
|
||||
|
@ -122,7 +127,9 @@ defmodule Phoenix.Presence.Client do
|
|||
defp handle_leave({left_key, meta}, {state, topic}) do
|
||||
updated_state = update_topics_state(:remove_presence_or_metas, state, topic, left_key, meta)
|
||||
|
||||
{:ok, updated_client_state} = state.client.handle_leave(topic, left_key, meta, state.client_state)
|
||||
{:ok, updated_client_state} =
|
||||
state.client.handle_leave(topic, left_key, meta, state.client_state)
|
||||
|
||||
updated_state = Map.put(updated_state, :client_state, updated_client_state)
|
||||
|
||||
{updated_state, topic}
|
||||
|
|
|
@ -15,25 +15,34 @@ defmodule LiveBeats.PresenceClient do
|
|||
end
|
||||
|
||||
@impl Phoenix.Presence.Client
|
||||
def handle_join(topic, key, _meta, state) do
|
||||
def handle_join(topic, _key, presence, state) do
|
||||
active_users_topic =
|
||||
topic
|
||||
|> profile_identifier()
|
||||
|> active_users_topic()
|
||||
|
||||
Phoenix.PubSub.local_broadcast(@pubsub, active_users_topic, {__MODULE__, %{user_joined: key}})
|
||||
Phoenix.PubSub.local_broadcast(
|
||||
@pubsub,
|
||||
active_users_topic,
|
||||
{__MODULE__, %{user_joined: presence}}
|
||||
)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl Phoenix.Presence.Client
|
||||
def handle_leave(topic, key, _meta, state) do
|
||||
def handle_leave(topic, _key, presence, state) do
|
||||
active_users_topic =
|
||||
topic
|
||||
|> profile_identifier()
|
||||
|> active_users_topic()
|
||||
|
||||
Phoenix.PubSub.local_broadcast(@pubsub, active_users_topic, {__MODULE__, %{user_left: key}})
|
||||
Phoenix.PubSub.local_broadcast(
|
||||
@pubsub,
|
||||
active_users_topic,
|
||||
{__MODULE__, %{user_left: presence}}
|
||||
)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,9 @@ defmodule LiveBeatsWeb.Presence do
|
|||
See the [`Phoenix.Presence`](http://hexdocs.pm/phoenix/Phoenix.Presence.html)
|
||||
docs for more details.
|
||||
"""
|
||||
use Phoenix.Presence, otp_app: :live_beats,
|
||||
pubsub_server: LiveBeats.PubSub
|
||||
use Phoenix.Presence,
|
||||
otp_app: :live_beats,
|
||||
pubsub_server: LiveBeats.PubSub
|
||||
|
||||
import Phoenix.LiveView.Helpers
|
||||
import LiveBeatsWeb.LiveHelpers
|
||||
|
@ -18,10 +19,16 @@ defmodule LiveBeatsWeb.Presence do
|
|||
~H"""
|
||||
<!-- users -->
|
||||
<div class="px-4 mt-6 sm:px-6 lg:px-8">
|
||||
<h2 class="text-gray-500 text-xs font-medium uppercase tracking-wide">Who's Listening</h2>
|
||||
<ul role="list" class="grid grid-cols-1 gap-4 sm:gap-4 sm:grid-cols-2 xl:grid-cols-5 mt-3" x-max="1">
|
||||
<h2 class="text-gray-500 text-xs font-medium uppercase tracking-wide">Here now</h2>
|
||||
<ul
|
||||
id="listening-now"
|
||||
phx-update="prepend"
|
||||
role="list"
|
||||
x-max="1"
|
||||
class="grid grid-cols-1 gap-4 sm:gap-4 sm:grid-cols-2 xl:grid-cols-5 mt-3"
|
||||
>
|
||||
<%= for presence <- @presences do %>
|
||||
<li class="relative col-span-1 flex shadow-sm rounded-md overflow-hidden">
|
||||
<li id={"presence-#{presence.id}"} class="relative col-span-1 flex shadow-sm rounded-md overflow-hidden">
|
||||
<.link navigate={profile_path(presence)} class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-md truncate">
|
||||
<img class="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-l-md bg-purple-600" src={presence.avatar_url} alt="">
|
||||
<div class="flex-1 flex items-center justify-between text-gray-900 text-sm font-medium hover:text-gray-600 pl-3">
|
||||
|
|
|
@ -88,7 +88,9 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
MediaLibrary.subscribe_to_profile(profile)
|
||||
Accounts.subscribe(current_user.id)
|
||||
LiveBeatsWeb.Presence.subscribe(profile)
|
||||
Phoenix.Presence.Client.track(topic(profile.user_id),
|
||||
|
||||
Phoenix.Presence.Client.track(
|
||||
topic(profile.user_id),
|
||||
current_user.id,
|
||||
%{}
|
||||
)
|
||||
|
@ -111,7 +113,7 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
|> list_songs()
|
||||
|> assign_presences()
|
||||
|
||||
{:ok, socket, temporary_assigns: [songs: []]}
|
||||
{:ok, socket, temporary_assigns: [songs: [], presences: []]}
|
||||
end
|
||||
|
||||
def handle_params(params, _url, socket) do
|
||||
|
@ -147,21 +149,14 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({LiveBeats.PresenceClient, %{user_joined: user_id}}, socket) do
|
||||
new_user = Accounts.get_user!(user_id)
|
||||
updated_presences =
|
||||
if new_user in socket.assigns.presences do
|
||||
socket.assigns.presences
|
||||
else
|
||||
[new_user | socket.assigns.presences]
|
||||
end
|
||||
{:noreply, assign(socket, :presences, updated_presences)}
|
||||
def handle_info({LiveBeats.PresenceClient, %{user_joined: presence}}, socket) do
|
||||
%{user: user} = presence
|
||||
{:noreply, update(socket, :presences, &[user | &1])}
|
||||
end
|
||||
|
||||
def handle_info({LiveBeats.PresenceClient, %{user_left: user_id}}, socket) do
|
||||
updated_presences = socket.assigns.presences
|
||||
|> Enum.reject(fn user -> user.id == String.to_integer(user_id) end)
|
||||
{:noreply, assign(socket, :presences, updated_presences)}
|
||||
def handle_info({LiveBeats.PresenceClient, %{user_left: presence}}, socket) do
|
||||
%{user: user} = presence
|
||||
{:noreply, push_event(socket, "remove-el", %{id: "presence-#{user.id}"})}
|
||||
end
|
||||
|
||||
def handle_info({Accounts, %Accounts.Events.ActiveProfileChanged{} = event}, socket) do
|
||||
|
@ -264,10 +259,11 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
end
|
||||
|
||||
defp assign_presences(socket) do
|
||||
presences = socket.assigns.profile.user_id
|
||||
|> topic()
|
||||
|> LiveBeats.PresenceClient.list()
|
||||
|> Enum.map(fn {_key, meta} -> meta.user end)
|
||||
presences =
|
||||
socket.assigns.profile.user_id
|
||||
|> topic()
|
||||
|> LiveBeats.PresenceClient.list()
|
||||
|> Enum.map(fn {_key, meta} -> meta.user end)
|
||||
|
||||
assign(socket, presences: presences)
|
||||
end
|
||||
|
|
|
@ -51,18 +51,13 @@ defmodule LiveBeatsWeb.ProfileLiveTest do
|
|||
}
|
||||
}) =~ "can't be blank"
|
||||
|
||||
assert {:ok, new_lv, html} =
|
||||
lv |> form("#song-form") |> render_submit() |> follow_redirect(conn)
|
||||
|
||||
assert_redirected(lv, "/#{current_user.username}")
|
||||
assert html =~ "1 song(s) uploaded"
|
||||
|
||||
assert html =~ "silence1s"
|
||||
assert lv |> form("#song-form") |> render_submit() =~ "silence1s"
|
||||
assert_patch(lv, "/#{current_user.username}")
|
||||
|
||||
# deleting songs
|
||||
|
||||
song = MediaLibrary.get_first_song(profile)
|
||||
assert new_lv |> element("#delete-modal-#{song.id}-confirm") |> render_click()
|
||||
assert lv |> element("#delete-modal-#{song.id}-confirm") |> render_click()
|
||||
|
||||
{:ok, refreshed_lv, _} = live(conn, LiveHelpers.profile_path(current_user))
|
||||
refute render(refreshed_lv) =~ "silence1s"
|
||||
|
|
Loading…
Reference in a new issue