1
0
mirror of https://github.com/algora-io/tv.git synced 2025-01-15 01:28:30 +02:00

add transcript tab (#4)

* init side panel

* reorganize stuff

* delete unused components

* fix some ids

* more fixes

* conditionally render transcript tab

* reorder some code

* delete redundant line

* remove unused alias
This commit is contained in:
Zafer Cesur 2024-03-10 16:51:18 +03:00 committed by GitHub
parent baa130f942
commit b7ed13e06f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 143 additions and 78 deletions

View File

@ -7,9 +7,9 @@ const init = () => {
socket.connect(); socket.connect();
const main = document.querySelector("body"); const main = document.querySelector("body");
const sidePanel = document.querySelector("#video-side-panel");
let channel; let channel;
let chatBox;
let chatInput; let chatInput;
let chatMessages; let chatMessages;
let handleSend; let handleSend;
@ -20,9 +20,9 @@ const init = () => {
chatInput.value = ""; chatInput.value = "";
chatInput.removeEventListener("keypress", handleSend); chatInput.removeEventListener("keypress", handleSend);
} }
chatBox.classList.add("lg:w-0"); sidePanel.classList.add("lg:w-0");
chatBox.classList.remove("lg:w-[24rem]"); sidePanel.classList.remove("lg:w-[24rem]");
chatBox.classList.remove("lg:flex"); sidePanel.classList.remove("lg:flex");
main.classList.remove("lg:mr-[24rem]"); main.classList.remove("lg:mr-[24rem]");
}; };
@ -31,15 +31,13 @@ const init = () => {
leave(channel); leave(channel);
} }
player = player;
channel = socket.channel(`room:${id}`, {}); channel = socket.channel(`room:${id}`, {});
chatBox = document.querySelector("#chat-box");
chatInput = document.querySelector("#chat-input"); chatInput = document.querySelector("#chat-input");
chatMessages = document.querySelector("#chat-messages"); chatMessages = document.querySelector("#chat-messages");
chatMessages.scrollTop = chatMessages.scrollHeight; chatMessages.scrollTop = chatMessages.scrollHeight;
chatBox.classList.add("lg:w-[24rem]"); sidePanel.classList.add("lg:w-[24rem]");
chatBox.classList.add("lg:flex"); sidePanel.classList.add("lg:flex");
chatBox.classList.remove("lg:w-0"); sidePanel.classList.remove("lg:w-0");
main.classList.add("lg:mr-[24rem]"); main.classList.add("lg:mr-[24rem]");
handleSend = (event) => { handleSend = (event) => {

View File

@ -145,6 +145,10 @@ defmodule Algora.Library do
|> Enum.map_join(":", fn count -> String.pad_leading("#{count}", 2, ["0"]) end) |> Enum.map_join(":", fn count -> String.pad_leading("#{count}", 2, ["0"]) end)
end end
def to_hhmmss(duration) when is_float(duration) do
to_hhmmss(trunc(duration))
end
def unsubscribe_to_channel(%Channel{} = channel) do def unsubscribe_to_channel(%Channel{} = channel) do
Phoenix.PubSub.unsubscribe(@pubsub, topic(channel.user_id)) Phoenix.PubSub.unsubscribe(@pubsub, topic(channel.user_id))
end end

View File

@ -79,7 +79,7 @@ defmodule AlgoraWeb.CoreComponents do
id={@id} id={@id}
class="cursor-pointer truncate" class="cursor-pointer truncate"
phx-click={ phx-click={
JS.push("join", value: %{video_id: @video.id}, target: "#chat-box") JS.push("show", value: %{video_id: @video.id}, target: "#side-panel")
|> JS.dispatch("js:play_video", |> JS.dispatch("js:play_video",
to: "#video-player", to: "#video-player",
detail: %{player: %{src: @video.url, type: Library.player_type(@video)}} detail: %{player: %{src: @video.url, type: Library.player_type(@video)}}
@ -117,7 +117,7 @@ defmodule AlgoraWeb.CoreComponents do
id={@id} id={@id}
class="cursor-pointer truncate" class="cursor-pointer truncate"
phx-click={ phx-click={
JS.push("join", value: %{video_id: @video.id}, target: "#chat-box") JS.push("show", value: %{video_id: @video.id}, target: "#side-panel")
|> JS.dispatch("js:play_video", |> JS.dispatch("js:play_video",
to: "#video-player", to: "#video-player",
detail: %{player: %{src: @video.url, type: Library.player_type(@video)}} detail: %{player: %{src: @video.url, type: Library.player_type(@video)}}

View File

@ -126,7 +126,9 @@
<.live_component module={AlgoraWeb.LayoutComponent} id="layout" /> <.live_component module={AlgoraWeb.LayoutComponent} id="layout" />
<%= live_render(@socket, AlgoraWeb.ChatLive, id: "chat", session: %{}, sticky: true) %> <aside id="video-side-panel" class="hidden fixed top-[64px] right-0 w-0 pr-4">
<%= live_render(@socket, AlgoraWeb.SidePanelLive, id: "side-panel", session: %{}) %>
</aside>
<main class="flex-1 relative z-0 overflow-y-auto focus:outline-none"> <main class="flex-1 relative z-0 overflow-y-auto focus:outline-none">
<%= live_render(@socket, AlgoraWeb.PlayerLive, id: "player", session: %{}, sticky: true) %> <%= live_render(@socket, AlgoraWeb.PlayerLive, id: "player", session: %{}, sticky: true) %>
<%= @inner_content %> <%= @inner_content %>

View File

@ -1,66 +0,0 @@
defmodule AlgoraWeb.ChatLive do
alias Algora.Chat.Message
alias Algora.{Library, Chat}
alias Algora.Library.Video
use AlgoraWeb, {:live_view, container: {:div, []}}
on_mount {AlgoraWeb.UserAuth, :current_user}
defp system_message?(%Message{} = message) do
message.sender_handle == "algora"
end
def render(assigns) do
~H"""
<aside id="chat-box" class="hidden fixed top-[64px] right-0 w-0 flex-col pr-4">
<div class="p-4 bg-gray-800/40 backdrop-blur-xl rounded-2xl shadow-inner shadow-white/[10%] border border-white/[15%]">
<div class="pb-2 text-center text-gray-400 text-xs font-medium uppercase tracking-wide">
Chat
</div>
<div
id="chat-messages"
class="text-sm break-words flex-1 overflow-y-auto h-[calc(100vh-11rem)]"
>
<div :for={message <- @messages} id={"message-#{message.id}"}>
<span class={"font-semibold #{if(system_message?(message), do: "text-emerald-400", else: "text-indigo-400")}"}>
<%= message.sender_handle %>:
</span>
<span class="font-medium text-gray-100">
<%= message.body %>
</span>
</div>
</div>
<input
:if={@current_user}
id="chat-input"
placeholder="Send a message"
disabled={@current_user == nil}
class="mt-2 bg-gray-950 h-[30px] text-white focus:outline-none focus:ring-purple-400 block w-full min-w-0 rounded-md sm:text-sm ring-1 ring-gray-600 px-2"
/>
<a
:if={!@current_user}
href={Algora.Github.authorize_url()}
class="mt-2 w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-400"
>
Sign in to chat
</a>
</div>
</aside>
"""
end
def mount(_params, _session, socket) do
{:ok, socket, layout: false, temporary_assigns: [messages: []]}
end
def handle_info({Library, _}, socket), do: {:noreply, socket}
def handle_event("join", %{"video_id" => video_id}, socket) do
socket =
socket
|> assign(messages: Chat.list_messages(%Video{id: video_id}))
|> push_event("join_chat", %{id: video_id})
{:noreply, socket}
end
end

View File

@ -0,0 +1,127 @@
defmodule AlgoraWeb.SidePanelLive do
use AlgoraWeb, {:live_view, container: {:div, class: "flex-1"}}
alias Algora.{Chat, Library}
on_mount {AlgoraWeb.UserAuth, :current_user}
def render(assigns) do
tabs =
[:chat]
|> append_if(length(assigns.subtitles) > 0, :transcript)
assigns = assigns |> assign(:tabs, tabs)
~H"""
<div class="p-4 bg-gray-800/40 w-[23rem] backdrop-blur-xl rounded-2xl shadow-inner shadow-white/[10%] border border-white/[15%]">
<div>
<ul class="pb-2 flex items-center justify-center gap-2 mx-auto text-gray-400">
<li :for={{tab, i} <- Enum.with_index(@tabs)}>
<button
id={"side-panel-tab-#{tab}"}
class={[
"text-xs font-semibold uppercase tracking-wide",
i == 0 && "active-tab text-white pointer-events-none"
]}
phx-click={
set_active_tab("#side-panel-tab-#{tab}")
|> set_active_content("#side-panel-content-#{tab}")
}
>
<%= tab %>
</button>
</li>
</ul>
</div>
<div>
<div
:for={{tab, i} <- Enum.with_index(@tabs)}
id={"side-panel-content-#{tab}"}
class={["side-panel-content", i != 0 && "hidden"]}
>
<div
:if={tab == :transcript}
id="transcript-subtitles"
class="text-sm break-words flex-1 overflow-y-auto h-[calc(100vh-11rem)]"
>
<div :for={subtitle <- @subtitles} id={"subtitle-#{subtitle.id}"}>
<span class="font-semibold text-indigo-400">
<%= Library.to_hhmmss(subtitle.start) %>
</span>
<span class="font-medium text-gray-100">
<%= subtitle.body %>
</span>
</div>
</div>
<div :if={tab == :chat}>
<div
id="chat-messages"
class="text-sm break-words flex-1 overflow-y-auto h-[calc(100vh-11rem)]"
>
<div :for={message <- @messages} id={"message-#{message.id}"}>
<span class={"font-semibold #{if(system_message?(message), do: "text-emerald-400", else: "text-indigo-400")}"}>
<%= message.sender_handle %>:
</span>
<span class="font-medium text-gray-100">
<%= message.body %>
</span>
</div>
</div>
<input
:if={@current_user}
id="chat-input"
placeholder="Send a message"
disabled={@current_user == nil}
class="mt-2 bg-gray-950 h-[30px] text-white focus:outline-none focus:ring-purple-400 block w-full min-w-0 rounded-md sm:text-sm ring-1 ring-gray-600 px-2"
/>
<a
:if={!@current_user}
href={Algora.Github.authorize_url()}
class="mt-2 w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-400"
>
Sign in to chat
</a>
</div>
</div>
</div>
</div>
"""
end
def mount(_params, _session, socket) do
{:ok, socket, temporary_assigns: [subtitles: [], messages: []]}
end
def handle_event("show", %{"video_id" => video_id}, socket) do
socket =
socket
|> assign(subtitles: Library.list_subtitles(%Library.Video{id: video_id}))
|> assign(messages: Chat.list_messages(%Library.Video{id: video_id}))
|> push_event("join_chat", %{id: video_id})
{:noreply, socket}
end
defp set_active_content(js \\ %JS{}, to) do
js
|> JS.hide(to: ".side-panel-content")
|> JS.show(to: to)
end
defp set_active_tab(js \\ %JS{}, tab) do
js
|> JS.remove_class("active-tab text-white pointer-events-none",
to: "#video-side-panel .active-tab"
)
|> JS.add_class("active-tab text-white pointer-events-none", to: tab)
end
defp system_message?(%Chat.Message{} = message) do
message.sender_handle == "algora"
end
defp append_if(list, cond, extra) do
if cond, do: list ++ [extra], else: list
end
end