Show active users in sidebar

This commit is contained in:
Chris McCord 2021-11-12 10:10:04 -05:00
parent b3fe0ab666
commit 5ca7357665
9 changed files with 151 additions and 177 deletions

View file

@ -192,6 +192,19 @@ defmodule LiveBeats.MediaLibrary do
|> Repo.all() |> Repo.all()
end end
def list_active_profiles(opts) do
from(s in Song,
inner_join: u in LiveBeats.Accounts.User,
on: s.user_id == u.id,
where: s.status in [:playing],
limit: ^Keyword.fetch!(opts, :limit),
order_by: [desc: s.updated_at],
select: struct(u, [:id, :username, :profile_tagline])
)
|> Repo.all()
|> Enum.map(&get_profile!/1)
end
def get_current_active_song(%Profile{user_id: user_id}) do def get_current_active_song(%Profile{user_id: user_id}) do
Repo.one(from s in Song, where: s.user_id == ^user_id and s.status in [:playing, :paused]) Repo.one(from s in Song, where: s.user_id == ^user_id and s.status in [:playing, :paused])
end end

View file

@ -135,5 +135,7 @@ defmodule LiveBeatsWeb.UserAuth do
defp maybe_store_return_to(conn), do: conn defp maybe_store_return_to(conn), do: conn
def signed_in_path(conn), do: Routes.song_index_path(conn, :index, conn.assigns.current_user.username) def signed_in_path(conn) do
LiveBeatsWeb.LiveHelpers.profile_path(conn.assigns.current_user)
end
end end

View file

@ -5,8 +5,15 @@ defmodule LiveBeatsWeb.LiveHelpers do
alias LiveBeatsWeb.Router.Helpers, as: Routes alias LiveBeatsWeb.Router.Helpers, as: Routes
alias Phoenix.LiveView.JS alias Phoenix.LiveView.JS
def home_path(socket) do alias LiveBeats.Accounts
Routes.song_index_path(socket, :index, socket.assigns.current_user.username) alias LiveBeats.MediaLibrary
def profile_path(%Accounts.User{} = current_user) do
Routes.song_index_path(LiveBeatsWeb.Endpoint, :index, current_user.username)
end
def profile_path(%MediaLibrary.Profile{} = profile) do
Routes.song_index_path(LiveBeatsWeb.Endpoint, :index, profile.username)
end end
def flash(%{kind: :error} = assigns) do def flash(%{kind: :error} = assigns) do

View file

@ -1,7 +1,9 @@
defmodule LiveBeatsWeb.Nav do defmodule LiveBeatsWeb.Nav do
import Phoenix.LiveView import Phoenix.LiveView
alias LiveBeats.MediaLibrary
def on_mount(:default, _params, _session, socket) do def on_mount(:default, _params, _session, socket) do
{:cont, assign(socket, genres: LiveBeats.MediaLibrary.list_genres())} {:cont, assign(socket, :active_users, MediaLibrary.list_active_profiles(limit: 20))}
end end
end end

View file

@ -38,7 +38,7 @@ defmodule LiveBeatsWeb.PlayerLive do
<div class="bg-gray-50 text-black dark:bg-gray-900 dark:text-white px-1 sm:px-3 lg:px-1 xl:px-3 grid grid-cols-5 items-center"> <div class="bg-gray-50 text-black dark:bg-gray-900 dark:text-white px-1 sm:px-3 lg:px-1 xl:px-3 grid grid-cols-5 items-center">
<%= if @profile do %> <%= if @profile do %>
<.link <.link
redirect_to={Routes.song_index_path(@socket, :index, @profile.username)} redirect_to={profile_path(@profile)}
class="mx-auto flex outline border-2 border-white border-opacity-20 rounded-md p-1 pr-2" class="mx-auto flex outline border-2 border-white border-opacity-20 rounded-md p-1 pr-2"
> >
<span class="mt-1"><.icon name={:user_circle}/></span> <span class="mt-1"><.icon name={:user_circle}/></span>

View file

@ -178,7 +178,7 @@ defmodule LiveBeatsWeb.SongLive.Index do
LayoutComponent.show_modal(UploadFormComponent, %{ LayoutComponent.show_modal(UploadFormComponent, %{
id: :new, id: :new,
confirm: {"Save", type: "submit", form: "song-form"}, confirm: {"Save", type: "submit", form: "song-form"},
patch_to: home_path(socket), patch_to: profile_path(socket.assigns.current_user),
song: socket.assigns.song, song: socket.assigns.song,
title: socket.assigns.page_title, title: socket.assigns.page_title,
current_user: socket.assigns.current_user current_user: socket.assigns.current_user

View file

@ -54,7 +54,7 @@ defmodule LiveBeatsWeb.SongLive.UploadFormComponent do
{:noreply, {:noreply,
socket socket
|> put_flash(:info, "#{map_size(songs)} song(s) uploaded") |> put_flash(:info, "#{map_size(songs)} song(s) uploaded")
|> push_redirect(to: home_path(socket))} |> push_redirect(to: profile_path(current_user))}
{:error, _reason} -> {:error, _reason} ->
{:noreply, socket} {:noreply, socket}

View file

@ -22,83 +22,11 @@
alt="Workflow"> alt="Workflow">
</div> </div>
<div class="mt-5 flex-1 h-0 overflow-y-auto"> <div class="mt-5 flex-1 h-0 overflow-y-auto">
<.sidebar_account_dropdown id="mobile-account-dropdown" current_user={@current_user}/>
<nav class="px-2"> <nav class="px-2">
<div class="space-y-1"> <.sidebar_nav_links current_user={@current_user}/>
<.sidebar_active_users id="desktop-active-users" users={@active_users}/>
<%= if @current_user do %>
<!-- User account dropdown -->
<div class="px-3 mt-6 relative inline-block text-left w-full">
<div>
<button type="button"
class="group w-full bg-gray-100 rounded-md px-3.5 py-2 text-sm text-left font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
phx-click={show_dropdown("#account-dropdown-mobile")}>
<span class="flex w-full justify-between items-center">
<span class="flex min-w-0 items-center justify-between space-x-3">
<img class="w-10 h-10 bg-gray-300 rounded-full flex-shrink-0"
src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=3&amp;w=256&amp;h=256&amp;q=80"
alt="">
<span class="flex-1 flex flex-col min-w-0">
<span class="text-gray-900 text-sm font-medium truncate"><%= @current_user.name %></span>
<span class="text-gray-500 text-sm truncate">@<%= @current_user.username %></span>
</span>
</span>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
</span>
</button>
</div>
<div id="account-dropdown-mobile" phx-click-away={hide_dropdown("#account-dropdown-mobile")} class="hidden z-10 mx-3 origin-top absolute right-0 left-0 mt-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-200 focus:outline-none" role="menu">
<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">View profile</a>
<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>
</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">Logout</a>
</div>
</div>
</div>
<% else %>
<%= live_redirect to: Routes.sign_in_path(@socket, :index),
class: "text-gray-600 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md" do %>
<svg class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"
xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Sign in
<% end %>
<% end %>
</div>
<div class="mt-8">
<h3 class="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider" id="mobile-teams-headline">
Rooms
</h3>
<div class="mt-1 space-y-1" role="group" aria-labelledby="mobile-teams-headline">
<%= for genre <- @genres do %>
<a href="#"
class="group flex items-center px-3 py-2 text-base leading-5 font-medium text-gray-600 rounded-md hover:text-gray-900 hover:bg-gray-50">
<span class="w-2.5 h-2.5 mr-4 bg-indigo-500 rounded-full" aria-hidden="true"></span>
<span class="truncate">
<%= genre.title %>
</span>
</a>
<% end %>
</div>
</div>
</nav> </nav>
</div> </div>
</div> </div>
@ -118,49 +46,7 @@
</div> </div>
<!-- Sidebar component, swap this element with another sidebar if you like --> <!-- Sidebar component, swap this element with another sidebar if you like -->
<div class="h-0 flex-1 flex flex-col overflow-y-auto"> <div class="h-0 flex-1 flex flex-col overflow-y-auto">
<%= if @current_user do %> <.sidebar_account_dropdown id="account-dropdown" current_user={@current_user}/>
<!-- User account dropdown -->
<div class="px-3 mt-6 relative inline-block text-left">
<div>
<button type="button"
class="group w-full bg-gray-100 rounded-md px-3.5 py-2 text-sm text-left font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
phx-click={show_dropdown("#account-dropdown")}>
<span class="flex w-full justify-between items-center">
<span class="flex min-w-0 items-center justify-between space-x-3">
<img class="w-10 h-10 bg-gray-300 rounded-full flex-shrink-0"
src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=3&amp;w=256&amp;h=256&amp;q=80"
alt="">
<span class="flex-1 flex flex-col min-w-0">
<span class="text-gray-900 text-sm font-medium truncate"><%= @current_user.name %></span>
<span class="text-gray-500 text-sm truncate">@<%= @current_user.username %></span>
</span>
</span>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
</span>
</button>
</div>
<div id="account-dropdown" phx-click-away={hide_dropdown("#account-dropdown")} class="hidden z-10 mx-3 origin-top absolute right-0 left-0 mt-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-200" role="menu" aria-expanded="true">
<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">View profile</a>
<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>
</div>
<div class="py-1" role="none">
<%= link "Logout", to: Routes.o_auth_callback_path(@socket, :sign_out), method: :delete, role: "menuitem", id: "options-menu-item-5", class: "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" %>
</div>
</div>
</div>
<% end %>
<!-- Sidebar Search --> <!-- Sidebar Search -->
<div class="px-3 mt-5"> <div class="px-3 mt-5">
<label for="search" class="sr-only">Search</label> <label for="search" class="sr-only">Search</label>
@ -180,57 +66,9 @@
</div> </div>
<!-- Navigation --> <!-- Navigation -->
<nav class="px-3 mt-6"> <nav class="px-3 mt-6">
<div class="space-y-1"> <.sidebar_nav_links current_user={@current_user}/>
<%= if @current_user do %>
<.link
redirect_to={Routes.song_index_path(@socket, :index, @current_user.username)}
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
>
<.icon name={:music_note} outlined class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"/>
My Songs
</.link>
<.link
redirect_to={Routes.settings_path(@socket, :edit)}
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
>
<.icon name={:adjustments} outlined class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"/>
Settings
</.link>
<% end %>
<%= unless @current_user do %>
<%= live_redirect to: Routes.sign_in_path(@socket, :index),
class: "text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md" do %>
<svg class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"
xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Sign in
<% end %>
<% end %>
</div>
<div class="mt-8">
<!-- Secondary navigation --> <!-- Secondary navigation -->
<h3 class="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider" id="desktop-teams-headline"> <.sidebar_active_users id="mobile-active-users" users={@active_users}/>
Rooms
</h3>
<div class="mt-1 space-y-1" role="group" aria-labelledby="desktop-teams-headline">
<%= for genre <- @genres do %>
<a href="#"
class="group flex items-center px-3 py-2 text-base leading-5 font-medium text-gray-600 rounded-md hover:text-gray-900 hover:bg-gray-50">
<span class="w-2.5 h-2.5 mr-4 bg-indigo-500 rounded-full" aria-hidden="true"></span>
<span class="truncate">
<%= genre.title %>
</span>
</a>
<% end %>
</div>
</div>
</nav> </nav>
</div> </div>
</div> </div>

View file

@ -4,4 +4,116 @@ defmodule LiveBeatsWeb.LayoutView do
# Phoenix LiveDashboard is available only in development by default, # Phoenix LiveDashboard is available only in development by default,
# so we instruct Elixir to not warn if the dashboard route is missing. # so we instruct Elixir to not warn if the dashboard route is missing.
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}} @compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
def sidebar_active_users(assigns) do
~H"""
<div class="mt-8">
<h3 class="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider" id={@id}>
Active Users
</h3>
<div class="mt-1 space-y-1" role="group" aria-labelledby={@id}>
<%= for user <- @users do %>
<.link redirect_to={profile_path(user)}
class="group flex items-center px-3 py-2 text-base leading-5 font-medium text-gray-600 rounded-md hover:text-gray-900 hover:bg-gray-50"
>
<span class="w-2.5 h-2.5 mr-4 bg-indigo-500 rounded-full" aria-hidden="true"></span>
<span class="truncate">
<%= user.username %>
</span>
</.link>
<% end %>
</div>
</div>
"""
end
def sidebar_nav_links(assigns) do
~H"""
<div class="space-y-1">
<%= if @current_user do %>
<.link
redirect_to={profile_path(@current_user)}
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
>
<.icon name={:music_note} outlined class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"/>
My Songs
</.link>
<.link
redirect_to={Routes.settings_path(LiveBeatsWeb.Endpoint, :edit)}
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
>
<.icon name={:adjustments} outlined class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"/>
Settings
</.link>
<% else %>
<.link redirect_to={Routes.sign_in_path(LiveBeatsWeb.Endpoint, :index)}
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
>
<svg class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"
xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Sign in
</.link>
<% end %>
</div>
"""
end
def sidebar_account_dropdown(assigns) do
~H"""
<!-- User account dropdown -->
<div class="px-3 mt-6 relative inline-block text-left">
<div>
<button type="button"
class="group w-full bg-gray-100 rounded-md px-3.5 py-2 text-sm text-left font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
phx-click={show_dropdown("##{@id}-dropdown")}>
<span class="flex w-full justify-between items-center">
<span class="flex min-w-0 items-center justify-between space-x-3">
<img class="w-10 h-10 bg-gray-300 rounded-full flex-shrink-0"
src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=3&amp;w=256&amp;h=256&amp;q=80"
alt="">
<span class="flex-1 flex flex-col min-w-0">
<span class="text-gray-900 text-sm font-medium truncate"><%= @current_user.name %></span>
<span class="text-gray-500 text-sm truncate">@<%= @current_user.username %></span>
</span>
</span>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
</span>
</button>
</div>
<div id={"#{@id}-dropdown"} phx-click-away={hide_dropdown("##{@id}-dropdown")} class="hidden z-10 mx-3 origin-top absolute right-0 left-0 mt-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-200" role="menu" aria-expanded="true">
<div class="py-1" role="none">
<.link
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>
</div>
<div class="py-1" role="none">
<.link
href={Routes.o_auth_callback_path(LiveBeatsWeb.Endpoint, :sign_out)}
method={:delete}
role="menuitem"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>Sign out</.link>
</div>
</div>
</div>
"""
end
end end