mirror of
https://github.com/algora-io/tv.git
synced 2025-02-14 01:59:50 +02:00
add subscriptions page (#43)
This commit is contained in:
parent
0da97054ca
commit
1cf1f37b35
@ -6,6 +6,17 @@ defmodule Algora.Events do
|
||||
alias Algora.Events.Event
|
||||
alias Algora.Accounts.{User, Identity}
|
||||
|
||||
def unsubscribe(user, channel_id) do
|
||||
%Event{
|
||||
actor_id: "user_#{user.id}",
|
||||
user_id: user.id,
|
||||
channel_id: channel_id,
|
||||
name: :unsubscribed
|
||||
}
|
||||
|> Event.changeset(%{})
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def toggle_subscription_event(user, show) do
|
||||
name = if subscribed?(user, show), do: :unsubscribed, else: :subscribed
|
||||
|
||||
@ -161,4 +172,31 @@ defmodule Algora.Events do
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def fetch_subscriptions(user) do
|
||||
# Get the latest relevant events (:subscribed and :unsubscribed) for each user
|
||||
latest_events_query =
|
||||
from(e in Event,
|
||||
where: e.user_id == ^user.id and e.name in [:subscribed, :unsubscribed],
|
||||
order_by: [desc: e.inserted_at],
|
||||
distinct: e.channel_id
|
||||
)
|
||||
|
||||
# Join user data and filter for :subscribed events
|
||||
from(e in subquery(latest_events_query),
|
||||
join: u in User,
|
||||
on: e.channel_id == u.id,
|
||||
join: i in Identity,
|
||||
on: i.user_id == u.id and i.provider == "github",
|
||||
select_merge: %{
|
||||
user_handle: u.handle,
|
||||
user_display_name: coalesce(u.name, u.handle),
|
||||
user_avatar_url: u.avatar_url,
|
||||
user_meta: i.provider_meta
|
||||
},
|
||||
where: e.name == :subscribed,
|
||||
order_by: [desc: e.inserted_at, desc: e.id]
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
end
|
||||
|
@ -13,6 +13,7 @@ defmodule Algora.Events.Event do
|
||||
field :user_email, :string, virtual: true
|
||||
field :user_avatar_url, :string, virtual: true
|
||||
field :user_github_handle, :string, virtual: true
|
||||
field :user_meta, :string, virtual: true
|
||||
field :first_video_id, :integer, virtual: true
|
||||
field :first_video_title, :string, virtual: true
|
||||
field :name, Ecto.Enum, values: [:subscribed, :unsubscribed, :watched, :rsvpd, :unrsvpd]
|
||||
|
@ -114,6 +114,29 @@ defmodule AlgoraWeb.Layouts do
|
||||
</svg>
|
||||
Studio
|
||||
</.link>
|
||||
<.link
|
||||
navigate="/subscriptions"
|
||||
class={
|
||||
"text-gray-200 hover:text-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md #{if @active_tab == :studio, do: "bg-gray-800", else: "hover:bg-gray-900"}"
|
||||
}
|
||||
aria-current={if @active_tab == :studio, do: "true", else: "false"}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="text-gray-400 group-hover:text-gray-300 mr-3 flex-shrink-0 h-6 w-6"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M19.5 12.572l-7.5 7.428l-7.5 -7.428a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" />
|
||||
</svg>
|
||||
Subscriptions
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/channel/settings"}
|
||||
class={
|
||||
@ -151,6 +174,8 @@ defmodule AlgoraWeb.Layouts do
|
||||
<:title><%= @current_user.name %></:title>
|
||||
<:subtitle>@<%= @current_user.handle %></:subtitle>
|
||||
<:link navigate={channel_path(@current_user)}>Channel</:link>
|
||||
<:link navigate={~p"/channel/studio"}>Studio</:link>
|
||||
<:link navigate={~p"/subscriptions"}>Subscriptions</:link>
|
||||
<:link navigate={~p"/channel/settings"}>Settings</:link>
|
||||
<:link href={~p"/auth/logout"} method={:delete}>Sign out</:link>
|
||||
</.dropdown>
|
||||
|
@ -191,6 +191,8 @@
|
||||
<.simple_dropdown id="navbar-account-dropdown">
|
||||
<:img src={@current_user.avatar_url} alt={@current_user.handle} />
|
||||
<:link navigate={channel_path(@current_user)}>Channel</:link>
|
||||
<:link navigate={~p"/channel/studio"}>Studio</:link>
|
||||
<:link navigate={~p"/subscriptions"}>Subscriptions</:link>
|
||||
<:link navigate={~p"/channel/settings"}>Settings</:link>
|
||||
<:link href={~p"/auth/logout"} method={:delete}>Sign out</:link>
|
||||
</.simple_dropdown>
|
||||
|
77
lib/algora_web/live/subscriptions_live.ex
Normal file
77
lib/algora_web/live/subscriptions_live.ex
Normal file
@ -0,0 +1,77 @@
|
||||
defmodule AlgoraWeb.SubscriptionsLive do
|
||||
use AlgoraWeb, :live_view
|
||||
|
||||
alias Algora.Events
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="max-w-5xl mx-auto pt-2 pb-6 px-4 sm:px-6 space-y-6">
|
||||
<.header>
|
||||
<h1 class="text-3xl font-semibold">Subscriptions</h1>
|
||||
<p class="text-base font-medium text-gray-200">View & manage your subscriptions</p>
|
||||
</.header>
|
||||
|
||||
<ul role="list" class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3">
|
||||
<li
|
||||
:for={subscription <- @subscriptions}
|
||||
class="col-span-1 flex flex-col rounded-2xl overflow-hidden bg-white/5 ring-1 ring-white/20 text-center shadow-lg relative group"
|
||||
>
|
||||
<img
|
||||
class="mx-auto absolute inset-0 flex-shrink-0 object-cover h-full w-full"
|
||||
src={subscription.user_avatar_url}
|
||||
alt=""
|
||||
/>
|
||||
<div class="absolute h-full w-full inset-0 bg-gradient-to-b from-transparent to-80% to-gray-950/80" />
|
||||
<div class="absolute inset-0 bg-purple-700/10" />
|
||||
<div class="relative">
|
||||
<div class="flex flex-1 flex-col min-h-[16rem]">
|
||||
<h3 class="mt-auto text-2xl font-semibold text-white [text-shadow:#000_10px_5px_10px]">
|
||||
<%= subscription.user_display_name %>
|
||||
</h3>
|
||||
<dl class="mt-1 flex flex-col justify-between px-2">
|
||||
<dt class="sr-only">Bio</dt>
|
||||
<dd class="text-sm text-gray-300 font-medium line-clamp-1">
|
||||
<%= subscription.user_meta["user"]["bio"] ||
|
||||
subscription.user_meta["user"]["company"] || "@#{subscription.user_handle}" %>
|
||||
</dd>
|
||||
</dl>
|
||||
<div class="-mt-2 group-hover:mt-2 grid grid-cols-2 divide-white/20 divide-x border-t border-transparent group-hover:border-white/20 transition-all">
|
||||
<.button class="opacity-0 rounded-none h-0 group-hover:opacity-100 group-hover:h-10 transition-all text-sm bg-gray-950/50 hover:bg-gray-950/75 text-white">
|
||||
<.link navigate={~p"/#{subscription.user_handle}"} class="relative">
|
||||
Watch
|
||||
</.link>
|
||||
</.button>
|
||||
<.button
|
||||
phx-click="unsubscribe"
|
||||
phx-value-id={subscription.channel_id}
|
||||
class="opacity-0 rounded-none h-0 group-hover:opacity-100 group-hover:h-10 transition-all text-sm bg-gray-950/50 hover:bg-gray-950/75 text-white"
|
||||
>
|
||||
Unsubscribe
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def mount(_params, _session, socket) do
|
||||
user = socket.assigns.current_user
|
||||
subscriptions = Events.fetch_subscriptions(user)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:subscriptions, subscriptions)}
|
||||
end
|
||||
|
||||
def handle_event("unsubscribe", %{"id" => id}, socket) do
|
||||
Events.unsubscribe(socket.assigns.current_user, String.to_integer(id))
|
||||
subscriptions = Events.fetch_subscriptions(socket.assigns.current_user)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:subscriptions, subscriptions)}
|
||||
end
|
||||
end
|
@ -89,6 +89,8 @@ defmodule AlgoraWeb.Router do
|
||||
|
||||
live_session :authenticated,
|
||||
on_mount: [{AlgoraWeb.UserAuth, :ensure_authenticated}, AlgoraWeb.Nav] do
|
||||
live "/subscriptions", SubscriptionsLive, :show
|
||||
|
||||
live "/channel/settings", SettingsLive, :edit
|
||||
live "/channel/studio", StudioLive, :show
|
||||
live "/channel/studio/upload", StudioLive, :upload
|
||||
|
Loading…
x
Reference in New Issue
Block a user