Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/add voume controls #117

Merged
merged 16 commits into from
Jan 15, 2024
2 changes: 1 addition & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ if (

const sorter = new Sortable(this.el, {
animation: 400,
delay: 20,
delay: 300,
dragClass: "drag-item",
forceFallback: true,
ghostClass: "drag-ghost",
Expand Down
75 changes: 75 additions & 0 deletions assets/js/youtube/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ const udpateTimeDisplays = (
export default {
backdrop_id: null,
endTimeTrackerId: null,
handleCallbackEvent: async (callbackEvent, args = {}) => {
if (callbackEvent) {
await this.pushEventTo(this.el, callbackEvent, args)
}
},
mounted() {
/**
* on_container_mounted
Expand Down Expand Up @@ -112,6 +117,11 @@ export default {
)
}, 1000)
hookContext.el.dataset['trackTimeInterval'] = trackTimeInterval

const backdrop = document.getElementById(this.backdropId)
backdrop.classList.add('opacity-0')
backdrop.classList.remove('opacity-50')

break
case YT.PlayerState.PAUSED:
console.debug("[Player State :: PAUSED")
Expand Down Expand Up @@ -270,6 +280,71 @@ export default {

scrollToElement(`${player.media_id}-item`)
})

/**
* change_volume
*
* Received when the player should change the volume level
*/
this.handleEvent('change_volume', async ({
volume_level: volumeLevel,
callback_event: callbackEvent = null
}) => {
console.debug('[Player :: change_volume', volumeLevel)
this.player.unMute()
this.player.setVolume(volumeLevel)

await this.handleCallbackEvent(callbackEvent)
})

/**
* mute
*
* Received when the player should mute
*/
this.handleEvent('mute', async ({callback_event: callbackEvent = null}) => {
console.debug('[Player :: mute')
this.player.mute()

await this.handleCallbackEvent(callbackEvent)
})

/**
* unmute
*
* Received when the player should unmute
*/
this.handleEvent('unmute', async ({
callback_event: callbackEvent = null
}) => {
console.debug('[Player :: unmute')
this.player.unMute()

await this.handleCallbackEvent(callbackEvent)
})

/**
* fullscreen
*
* Switches to fullscreen mode
*/
this.handleEvent('fullscreen', () => {
console.debug('[Player :: fullscreen')
const videoIframe = this.player.getIframe()

console.log("iframe", videoIframe)

const requestFullScreen =
videoIframe.requestFullScreen
|| videoIframe.mozRequestFullScreen
|| videoIframe.webkitRequestFullScreen

console.log(requestFullScreen)

if (requestFullScreen) {
requestFullScreen.bind(videoIframe)();
}
})
},
player: null,
playerContainerId: null,
Expand Down
2 changes: 1 addition & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ config :livedj, Livedj.Repo,
config :livedj, LivedjWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
http: [ip: {0, 0, 0, 0}, port: 4000],
check_origin: false,
code_reloader: true,
debug_errors: true,
Expand Down
6 changes: 4 additions & 2 deletions lib/livedj/sessions/channels.ex
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,16 @@ defmodule Livedj.Sessions.Channels do
"""
@spec broadcast_player_play!(binary(), Livedj.Sessions.Player.t()) :: :ok
def broadcast_player_play!(room_id, %Livedj.Sessions.Player{} = player),
do: broadcast!(player_topic(room_id), {player_play_event(), player})
do:
broadcast!(player_topic(room_id), {player_play_event(), room_id, player})

@doc """
Broadcasts a #{@player_pause} message to the player topic.
"""
@spec broadcast_player_pause!(binary(), Livedj.Sessions.Player.t()) :: :ok
def broadcast_player_pause!(room_id, %Livedj.Sessions.Player{} = player),
do: broadcast!(player_topic(room_id), {player_pause_event(), player})
do:
broadcast!(player_topic(room_id), {player_pause_event(), room_id, player})

# ----------------------------------------------------------------------------
# Playlist brodcasting
Expand Down
9 changes: 5 additions & 4 deletions lib/livedj_web/components/custom_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ defmodule LivedjWeb.CustomComponents do
"""
def room_grid(assigns) do
assigns =
with %{modules: %Phoenix.LiveView.LiveStream{}} <- assigns do
with %{modules: _modules} <- assigns do
assign(
assigns,
module_id: assigns.module_id || fn {id, _item} -> id end
module_id: assigns.module_id || fn %{id: id} -> id end
)
end

Expand All @@ -118,8 +118,9 @@ defmodule LivedjWeb.CustomComponents do
class="
group
h-52 w-40 rounded-lg
bg-zinc-50 hover:bg-zinc-200 border-[1px] border-zinc-200 dark:border-0
dark:bg-zinc-800 dark:hover:bg-zinc-700
transition duration-300
bg-zinc-50 hover:brightness-90 border-[1px] border-zinc-200 dark:border-0
dark:bg-zinc-800 dark:hover:bg-zinc-800 dark:hover:brightness-110
"
>
<div
Expand Down
2 changes: 1 addition & 1 deletion lib/livedj_web/components/layouts/app.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<a href="/">
<.livedj_logo theme={@theme} />
</a>
<div>
<div class="cursor-default">
<.version_pill version={Application.spec(:livedj, :vsn)} />
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions lib/livedj_web/components/layouts/session.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<.toggle_theme_button theme={@theme} />
</div>
</div>
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
<%!-- <ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
<%= if @current_user do %>
<li class="text-[0.8125rem] leading-6">
<.text><%= @current_user.email %></.text>
Expand Down Expand Up @@ -51,7 +51,7 @@
</.link>
</li>
<% end %>
</ul>
</ul> --%>
</header>

<main class="sm:py-10 lg:px-8">
Expand Down
1 change: 1 addition & 0 deletions lib/livedj_web/components/list_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ defmodule LivedjWeb.ListComponent do
<button
type="button"
class="
cursor-default
transition-all duration-300
w-6 h-6 rounded mr-1 flex-none hover:bg-gray-300 hover:dark:bg-gray-700
text-zinc-900 dark:text-zinc-100 hover:text-red-500 hover:dark:text-red-500
Expand Down
45 changes: 45 additions & 0 deletions lib/livedj_web/components/player_preview.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
defmodule LivedjWeb.PlayerPreview do
@moduledoc false

use LivedjWeb, :live_component

alias Livedj.Sessions.Player

def render(assigns) do
~H"""
<div class="h-40 w-40 py-2 px-2">
<%= if player?(@player) && @player.media_thumbnail_url != "" do %>
<img
class="h-full w-full rounded-md ring-0 ring-white"
src={get_player_thumbnail(@player)}
/>
<% else %>
<div class={"
h-full w-full rounded-md ring-0 ring-white bg-gray-200 dark:bg-gray-800
flex flex-wrap justify-center content-center
#{if paused?(@player), do: "animate-pulse"}
"}>
<.icon name="hero-musical-note" class="h-12 w-12 text-zinc-500" />
</div>
<% end %>
</div>
"""
end

def update(assigns, socket) do
{:ok,
socket
|> assign(assigns)}
end

defp player?(nil), do: false
defp player?(%Player{}), do: true

defp paused?(%Player{state: :paused}), do: true
defp paused?(_player), do: false

defp get_player_thumbnail(%Livedj.Sessions.Player{
media_thumbnail_url: media_thumbnail_url
}),
do: media_thumbnail_url
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule LivedjWeb.Components.PlayerControls.FullscreenControlComponent do
@moduledoc false

use LivedjWeb, :live_component

@impl true
def render(assigns) do
~H"""
<div phx-click="on_click" phx-target={@myself}>
<%= PhoenixInlineSvg.Helpers.svg_image(
LivedjWeb.Endpoint,
"fullscreen",
"icons/misc",
class: "
h-4 w-4 stroke-2 cursor-pointer
fill-zinc-700 hover:fill-zinc-900 focus:fill-zinc-700 active:fill-zinc-700
dark:fill-zinc-300 dark:hover:fill-zinc-50 dark:focus:fill-zinc-300 dark:active:fill-zinc-300
scale-100 hover:scale-[1.1] focus:scale-100 active:scale-100
"
) %>
</div>
"""
end

@impl true
def update(assigns, socket) do
{:ok, assign(socket, assigns)}
end

@impl true
def handle_event("on_click", _params, socket) do
{:noreply, push_event(socket, "fullscreen", %{})}
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
defmodule LivedjWeb.Components.PlayerControls.VolumeControlComponent do
@moduledoc false

use LivedjWeb, :live_component

@impl true
def render(assigns) do
~H"""
<div class="
hidden sm:inline-flex w-24
fill-zinc-700 hover:fill-zinc-900 focus:fill-zinc-700 active:fill-zinc-700
dark:fill-zinc-300 dark:hover:fill-zinc-50 dark:focus:fill-zinc-300 dark:active:fill-zinc-300
">
<div class="m-2" phx-click="on_volume_click" phx-target={@myself}>
<%= PhoenixInlineSvg.Helpers.svg_image(
LivedjWeb.Endpoint,
get_volume_icon(@muted?, @level),
"icons/volume",
class: "
h-5 w-5

"
) %>
</div>
<div class="self-center">
<.form
:let={f}
for={@player}
id="volume-controls-slider"
class=""
phx-target={@myself}
phx-change="on_volume_change"
>
<.input
field={f[:volume]}
class="seek-bar w-full !m-0 shadow-none !bg-transparent"
id="volume-slider"
value={if @muted?, do: 0, else: @level}
type="range"
min="0"
max="100"
step="1"
phx-debounce={500}
phx-value-key="volume"
/>
</.form>
</div>
</div>
"""
end

@impl true
def mount(socket) do
{:ok,
socket
|> assign(player: to_form(%{}))}
end

@impl true
def update(assigns, socket) do
{:ok,
socket
|> assign(assigns)}
end

@impl true
def handle_event(
"on_volume_click",
_params,
%{assigns: %{muted?: muted?}} = socket
) do
{:noreply,
socket
|> assign(muted?: !muted?)
|> push_event(if(muted?, do: "unmute", else: "mute"), %{})}
end

@impl true
def handle_event("on_volume_change", %{"volume" => volume}, socket) do
{:noreply,
socket
|> assign(
level: String.to_integer(volume),
muted?: false
)
|> push_event("change_volume", %{volume_level: volume})}
end

defp get_volume_icon(true, _volume_level) do
"speaker-0"
end

defp get_volume_icon(_muted?, volume_level) do
get_volume_icon_by_volume_level(volume_level)
end

defp get_volume_icon_by_volume_level(0), do: "speaker-0"

defp get_volume_icon_by_volume_level(volume) when volume <= 10,
do: "speaker-1"

defp get_volume_icon_by_volume_level(volume) when volume <= 30,
do: "speaker-2"

defp get_volume_icon_by_volume_level(volume) when volume <= 70,
do: "speaker-3"

defp get_volume_icon_by_volume_level(_volume), do: "speaker-4"
end
Loading
Loading