1
0
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:
Zafer Cesur 2024-05-30 12:36:19 +03:00 committed by GitHub
parent 0da97054ca
commit 1cf1f37b35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 145 additions and 0 deletions

View File

@ -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

View File

@ -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]

View File

@ -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>

View File

@ -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>

View 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

View File

@ -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