WIP work on fullscreen player
This commit is contained in:
parent
9f1af761e5
commit
4d9ba0effe
10 changed files with 324 additions and 114 deletions
|
@ -1,3 +1,7 @@
|
||||||
export function requestAnimationFrame(callback) {
|
export function requestAnimationFrame(callback) {
|
||||||
globalThis.requestAnimationFrame(callback);
|
globalThis.requestAnimationFrame(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function preventPopstate() {
|
||||||
|
globalThis.history.pushState({}, "");
|
||||||
|
}
|
||||||
|
|
|
@ -266,22 +266,12 @@ pub fn view(model: Model) {
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
div(
|
case model.play_status {
|
||||||
[
|
HasTracks(PlayInfo(player: player, ..)) ->
|
||||||
attribute.id("authed-view-player"),
|
player.view(player)
|
||||||
attribute.class("glass-bg glass-shadow glass-blur glass-border"),
|
|> element.map(PlayerMsg)
|
||||||
],
|
NoTracks -> text("")
|
||||||
[
|
},
|
||||||
case model.play_status {
|
|
||||||
HasTracks(PlayInfo(player: player, ..)) ->
|
|
||||||
div([attribute.id("player")], [
|
|
||||||
player.view(player)
|
|
||||||
|> element.map(PlayerMsg),
|
|
||||||
])
|
|
||||||
NoTracks -> text("")
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import elektrofoni
|
||||||
import elekf/utils/lustre
|
import elekf/utils/lustre
|
||||||
import elekf/web/common
|
import elekf/web/common
|
||||||
import elekf/web/volume
|
import elekf/web/volume
|
||||||
|
import elekf/web/components/player/full_screen
|
||||||
import elekf/web/components/player/small
|
import elekf/web/components/player/small
|
||||||
import elekf/web/components/player/model.{
|
import elekf/web/components/player/model.{
|
||||||
type AudioContext, type GainNode, type MediaElementAudioSourceNode, type Model,
|
type AudioContext, type GainNode, type MediaElementAudioSourceNode, type Model,
|
||||||
|
@ -42,10 +43,10 @@ pub type Msg {
|
||||||
UpdateRequestConfig(RequestConfig)
|
UpdateRequestConfig(RequestConfig)
|
||||||
BackwardSeekRequested(Float)
|
BackwardSeekRequested(Float)
|
||||||
ForwardSeekRequested(Float)
|
ForwardSeekRequested(Float)
|
||||||
CreateMediaElementAudioSourceNode
|
ComponentInitialised
|
||||||
FullScreenToggled
|
|
||||||
ActionTriggered(actions.Action)
|
ActionTriggered(actions.Action)
|
||||||
PositionSelected(Int)
|
PositionSelected(Int)
|
||||||
|
FullScreenEscaped
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
@ -92,9 +93,7 @@ pub fn init(
|
||||||
let seek_to = option.unwrap(data.seek_time, 0.0)
|
let seek_to = option.unwrap(data.seek_time, 0.0)
|
||||||
dispatch(PositionSelected(float.round(seek_to)))
|
dispatch(PositionSelected(float.round(seek_to)))
|
||||||
})
|
})
|
||||||
lustre.after_next_render(fn() {
|
lustre.after_next_render(fn() { dispatch(ComponentInitialised) })
|
||||||
dispatch(CreateMediaElementAudioSourceNode)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
start_play(settings, artist, album, track)
|
start_play(settings, artist, album, track)
|
||||||
|
@ -157,6 +156,22 @@ pub fn update(model: Model, msg) {
|
||||||
let pos = current_track_position()
|
let pos = current_track_position()
|
||||||
#(Model(..model, position: pos), effect.none())
|
#(Model(..model, position: pos), effect.none())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actions.ToggleFullScreen -> {
|
||||||
|
let #(new_mode, toggle_effect) = case model.view_mode {
|
||||||
|
model.Small -> #(
|
||||||
|
model.FullScreen,
|
||||||
|
effect.from(fn(dispatch) { register_back_preventor(dispatch) }),
|
||||||
|
)
|
||||||
|
|
||||||
|
model.FullScreen -> {
|
||||||
|
unregister_back_preventor()
|
||||||
|
#(model.Small, effect.none())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#(Model(..model, view_mode: new_mode), toggle_effect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StartPlay(track_id, track) -> {
|
StartPlay(track_id, track) -> {
|
||||||
|
@ -231,20 +246,15 @@ pub fn update(model: Model, msg) {
|
||||||
skip_to_time(pos)
|
skip_to_time(pos)
|
||||||
#(Model(..model, position: pos), effect.none())
|
#(Model(..model, position: pos), effect.none())
|
||||||
}
|
}
|
||||||
CreateMediaElementAudioSourceNode -> {
|
ComponentInitialised -> {
|
||||||
let source = create_media_element_source(model.audio_context)
|
let source = create_media_element_source(model.audio_context)
|
||||||
connect_gain(model.gain_node, model.audio_context, source)
|
connect_gain(model.gain_node, model.audio_context, source)
|
||||||
|
|
||||||
#(Model(..model, audio_source: option.Some(source)), effect.none())
|
#(Model(..model, audio_source: option.Some(source)), effect.none())
|
||||||
}
|
}
|
||||||
|
FullScreenEscaped -> {
|
||||||
FullScreenToggled -> {
|
unregister_back_preventor()
|
||||||
let new_mode = case model.view_mode {
|
#(Model(..model, view_mode: model.Small), effect.none())
|
||||||
model.Small -> model.FullScreen
|
|
||||||
model.FullScreen -> model.Small
|
|
||||||
}
|
|
||||||
|
|
||||||
#(Model(..model, view_mode: new_mode), effect.none())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,9 +281,11 @@ pub fn view(model: Model) {
|
||||||
model.Small ->
|
model.Small ->
|
||||||
small.view(model)
|
small.view(model)
|
||||||
|> element.map(ActionTriggered)
|
|> element.map(ActionTriggered)
|
||||||
|
model.FullScreen ->
|
||||||
|
full_screen.view(model)
|
||||||
|
|> element.map(ActionTriggered)
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
//FullScreen -> full_screen.view()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn form_url(
|
fn form_url(
|
||||||
|
@ -308,6 +320,14 @@ fn event_timeupdate(_: a) {
|
||||||
Ok(UpdateTime(current_time()))
|
Ok(UpdateTime(current_time()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_back_preventor(dispatch: fn(Msg) -> Nil) {
|
||||||
|
enable_prevent_popstate(fn() { dispatch(FullScreenEscaped) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unregister_back_preventor() {
|
||||||
|
disable_prevent_popstate()
|
||||||
|
}
|
||||||
|
|
||||||
fn start_play(
|
fn start_play(
|
||||||
settings: common.Settings,
|
settings: common.Settings,
|
||||||
artist: Artist,
|
artist: Artist,
|
||||||
|
@ -383,3 +403,9 @@ fn connect_gain(
|
||||||
|
|
||||||
@external(javascript, "../../../player_ffi.mjs", "linearRampToValue")
|
@external(javascript, "../../../player_ffi.mjs", "linearRampToValue")
|
||||||
fn linear_ramp_to_value(node: GainNode, gain: Float, at: Float) -> Nil
|
fn linear_ramp_to_value(node: GainNode, gain: Float, at: Float) -> Nil
|
||||||
|
|
||||||
|
@external(javascript, "../../../player_ffi.mjs", "enablePreventPopstate")
|
||||||
|
fn enable_prevent_popstate(callback: fn() -> Nil) -> Nil
|
||||||
|
|
||||||
|
@external(javascript, "../../../player_ffi.mjs", "disablePreventPopstate")
|
||||||
|
fn disable_prevent_popstate() -> Nil
|
||||||
|
|
|
@ -12,4 +12,5 @@ pub type Action {
|
||||||
StartUserSkip
|
StartUserSkip
|
||||||
EndUserSkip
|
EndUserSkip
|
||||||
SelectPosition(type_: PositionSelection)
|
SelectPosition(type_: PositionSelection)
|
||||||
|
ToggleFullScreen
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,107 @@
|
||||||
pub fn view() {
|
import gleam/int
|
||||||
todo
|
import gleam/option
|
||||||
|
import lustre/element.{text}
|
||||||
|
import lustre/element/html.{div, input, p}
|
||||||
|
import lustre/attribute
|
||||||
|
import lustre/event
|
||||||
|
import elekf/web/components/icon.{Alt, icon}
|
||||||
|
import elekf/web/components/track_length.{track_length}
|
||||||
|
import elekf/web/components/button_group
|
||||||
|
import elekf/web/components/button
|
||||||
|
import elekf/web/components/thumbnail
|
||||||
|
import elekf/web/components/player/actions.{
|
||||||
|
Commit, EndUserSkip, Ephemeral, NextTrack, Pause, Play, PrevTrack,
|
||||||
|
SelectPosition, StartUserSkip, ToggleFullScreen,
|
||||||
|
}
|
||||||
|
import elekf/web/components/player/model.{type Model}
|
||||||
|
|
||||||
|
pub fn view(model: Model) {
|
||||||
|
let is_playing = model.state == model.Playing
|
||||||
|
let current_time_padding = case model.track.length > 3600 {
|
||||||
|
True -> track_length.Hours
|
||||||
|
False -> track_length.Auto
|
||||||
|
}
|
||||||
|
|
||||||
|
div(
|
||||||
|
[
|
||||||
|
attribute.id("full-player-wrapper"),
|
||||||
|
attribute.class("player-wrapper glass-bg glass-blur"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
thumbnail.maybe_view(
|
||||||
|
option.Some(model.settings),
|
||||||
|
"full-player-artwork",
|
||||||
|
"full-player-thumbnail",
|
||||||
|
"full-player-thumbnail full-player-thumbnail-placeholder",
|
||||||
|
model.track.artwork_id,
|
||||||
|
model.album.name,
|
||||||
|
),
|
||||||
|
p([attribute.id("full-player-track-name")], [text(model.track.title)]),
|
||||||
|
p([attribute.id("full-player-artist-name")], [text(model.artist.name)]),
|
||||||
|
p([attribute.id("full-player-album-name")], [text(model.album.name)]),
|
||||||
|
div([attribute.id("player-controls")], [
|
||||||
|
button_group.view("", [attribute.id("player-controls-buttons")], [
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-previous"), event.on_click(PrevTrack)],
|
||||||
|
[icon("skip-backward-fill", Alt("Previous"))],
|
||||||
|
),
|
||||||
|
case is_playing {
|
||||||
|
True ->
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-play-pause"), event.on_click(Pause)],
|
||||||
|
[icon("pause-fill", Alt("Pause"))],
|
||||||
|
)
|
||||||
|
False ->
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-play-pause"), event.on_click(Play)],
|
||||||
|
[icon("play-fill", Alt("Play"))],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-next"), event.on_click(NextTrack)],
|
||||||
|
[icon("skip-forward-fill", Alt("Next"))],
|
||||||
|
),
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
attribute.id("player-fullscreen-toggle"),
|
||||||
|
event.on_click(ToggleFullScreen),
|
||||||
|
],
|
||||||
|
[icon("arrows-angle-contract", Alt("Small player"))],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
p(
|
||||||
|
[
|
||||||
|
attribute.id("player-time-elapsed"),
|
||||||
|
attribute.class("player-time"),
|
||||||
|
attribute.attribute("aria-label", "Time elapsed"),
|
||||||
|
],
|
||||||
|
[track_length(model.position, current_time_padding)],
|
||||||
|
),
|
||||||
|
p(
|
||||||
|
[
|
||||||
|
attribute.id("player-time-total"),
|
||||||
|
attribute.class("player-time"),
|
||||||
|
attribute.attribute("aria-label", "Total time"),
|
||||||
|
],
|
||||||
|
[track_length(model.track.length, track_length.Auto)],
|
||||||
|
),
|
||||||
|
input([
|
||||||
|
attribute.id("player-track"),
|
||||||
|
attribute.type_("range"),
|
||||||
|
attribute.min("0"),
|
||||||
|
attribute.max(int.to_string(model.track.length)),
|
||||||
|
attribute.attribute("aria-label", "Track position"),
|
||||||
|
event.on("change", fn(_) { Ok(SelectPosition(Commit)) }),
|
||||||
|
event.on_mouse_down(StartUserSkip),
|
||||||
|
event.on_mouse_up(EndUserSkip),
|
||||||
|
event.on_input(fn(_) { SelectPosition(Ephemeral) }),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import gleam/uri
|
import gleam/uri
|
||||||
import gleam/option
|
import gleam/option
|
||||||
|
import plinth/browser/event
|
||||||
import ibroadcast/authed_request.{type RequestConfig}
|
import ibroadcast/authed_request.{type RequestConfig}
|
||||||
import elekf/web/common
|
import elekf/web/common
|
||||||
import elekf/library.{type Library}
|
import elekf/library.{type Library}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import elekf/web/components/button
|
||||||
import elekf/web/components/thumbnail
|
import elekf/web/components/thumbnail
|
||||||
import elekf/web/components/player/actions.{
|
import elekf/web/components/player/actions.{
|
||||||
Commit, EndUserSkip, Ephemeral, NextTrack, Pause, Play, PrevTrack,
|
Commit, EndUserSkip, Ephemeral, NextTrack, Pause, Play, PrevTrack,
|
||||||
SelectPosition, StartUserSkip,
|
SelectPosition, StartUserSkip, ToggleFullScreen,
|
||||||
}
|
}
|
||||||
import elekf/web/components/player/model.{type Model}
|
import elekf/web/components/player/model.{type Model}
|
||||||
|
|
||||||
|
@ -22,72 +22,88 @@ pub fn view(model: Model) {
|
||||||
False -> track_length.Auto
|
False -> track_length.Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
div([attribute.id("small-player-wrapper")], [
|
div(
|
||||||
thumbnail.maybe_view(
|
[
|
||||||
option.Some(model.settings),
|
attribute.id("small-player-wrapper"),
|
||||||
"small-player-artwork",
|
attribute.class(
|
||||||
"small-player-thumbnail",
|
"player-wrapper glass-bg glass-shadow glass-blur glass-border",
|
||||||
"small-player-thumbnail small-player-thumbnail-placeholder",
|
|
||||||
model.track.artwork_id,
|
|
||||||
model.album.name,
|
|
||||||
),
|
|
||||||
p([attribute.id("small-player-track-name")], [text(model.track.title)]),
|
|
||||||
p([attribute.id("small-player-artist-name")], [text(model.artist.name)]),
|
|
||||||
p([attribute.id("small-player-album-name")], [text(model.album.name)]),
|
|
||||||
div([attribute.id("player-controls")], [
|
|
||||||
button_group.view("", [attribute.id("player-controls-buttons")], [
|
|
||||||
button.view(
|
|
||||||
"button-small",
|
|
||||||
[attribute.id("player-previous"), event.on_click(PrevTrack)],
|
|
||||||
[icon("skip-backward-fill", Alt("Previous"))],
|
|
||||||
),
|
|
||||||
case is_playing {
|
|
||||||
True ->
|
|
||||||
button.view(
|
|
||||||
"button-small",
|
|
||||||
[attribute.id("player-play-pause"), event.on_click(Pause)],
|
|
||||||
[icon("pause-fill", Alt("Pause"))],
|
|
||||||
)
|
|
||||||
False ->
|
|
||||||
button.view(
|
|
||||||
"button-small",
|
|
||||||
[attribute.id("player-play-pause"), event.on_click(Play)],
|
|
||||||
[icon("play-fill", Alt("Play"))],
|
|
||||||
)
|
|
||||||
},
|
|
||||||
button.view(
|
|
||||||
"button-small",
|
|
||||||
[attribute.id("player-next"), event.on_click(NextTrack)],
|
|
||||||
[icon("skip-forward-fill", Alt("Next"))],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
p(
|
|
||||||
[
|
|
||||||
attribute.id("player-time-elapsed"),
|
|
||||||
attribute.class("player-time"),
|
|
||||||
attribute.attribute("aria-label", "Time elapsed"),
|
|
||||||
],
|
|
||||||
[track_length(model.position, current_time_padding)],
|
|
||||||
),
|
),
|
||||||
p(
|
],
|
||||||
[
|
[
|
||||||
attribute.id("player-time-total"),
|
thumbnail.maybe_view(
|
||||||
attribute.class("player-time"),
|
option.Some(model.settings),
|
||||||
attribute.attribute("aria-label", "Total time"),
|
"small-player-artwork",
|
||||||
],
|
"small-player-thumbnail",
|
||||||
[track_length(model.track.length, track_length.Auto)],
|
"small-player-thumbnail small-player-thumbnail-placeholder",
|
||||||
|
model.track.artwork_id,
|
||||||
|
model.album.name,
|
||||||
),
|
),
|
||||||
input([
|
p([attribute.id("small-player-track-name")], [text(model.track.title)]),
|
||||||
attribute.id("player-track"),
|
p([attribute.id("small-player-artist-name")], [text(model.artist.name)]),
|
||||||
attribute.type_("range"),
|
p([attribute.id("small-player-album-name")], [text(model.album.name)]),
|
||||||
attribute.min("0"),
|
div([attribute.id("player-controls")], [
|
||||||
attribute.max(int.to_string(model.track.length)),
|
button_group.view("", [attribute.id("player-controls-buttons")], [
|
||||||
attribute.attribute("aria-label", "Track position"),
|
button.view(
|
||||||
event.on("change", fn(_) { Ok(SelectPosition(Commit)) }),
|
"",
|
||||||
event.on_mouse_down(StartUserSkip),
|
[attribute.id("player-previous"), event.on_click(PrevTrack)],
|
||||||
event.on_mouse_up(EndUserSkip),
|
[icon("skip-backward-fill", Alt("Previous"))],
|
||||||
event.on_input(fn(_) { SelectPosition(Ephemeral) }),
|
),
|
||||||
|
case is_playing {
|
||||||
|
True ->
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-play-pause"), event.on_click(Pause)],
|
||||||
|
[icon("pause-fill", Alt("Pause"))],
|
||||||
|
)
|
||||||
|
False ->
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-play-pause"), event.on_click(Play)],
|
||||||
|
[icon("play-fill", Alt("Play"))],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[attribute.id("player-next"), event.on_click(NextTrack)],
|
||||||
|
[icon("skip-forward-fill", Alt("Next"))],
|
||||||
|
),
|
||||||
|
button.view(
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
attribute.id("player-fullscreen-toggle"),
|
||||||
|
event.on_click(ToggleFullScreen),
|
||||||
|
],
|
||||||
|
[icon("arrows-angle-expand", Alt("Full screen player"))],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
p(
|
||||||
|
[
|
||||||
|
attribute.id("player-time-elapsed"),
|
||||||
|
attribute.class("player-time"),
|
||||||
|
attribute.attribute("aria-label", "Time elapsed"),
|
||||||
|
],
|
||||||
|
[track_length(model.position, current_time_padding)],
|
||||||
|
),
|
||||||
|
p(
|
||||||
|
[
|
||||||
|
attribute.id("player-time-total"),
|
||||||
|
attribute.class("player-time"),
|
||||||
|
attribute.attribute("aria-label", "Total time"),
|
||||||
|
],
|
||||||
|
[track_length(model.track.length, track_length.Auto)],
|
||||||
|
),
|
||||||
|
input([
|
||||||
|
attribute.id("player-track"),
|
||||||
|
attribute.type_("range"),
|
||||||
|
attribute.min("0"),
|
||||||
|
attribute.max(int.to_string(model.track.length)),
|
||||||
|
attribute.attribute("aria-label", "Track position"),
|
||||||
|
event.on("change", fn(_) { Ok(SelectPosition(Commit)) }),
|
||||||
|
event.on_mouse_down(StartUserSkip),
|
||||||
|
event.on_mouse_up(EndUserSkip),
|
||||||
|
event.on_input(fn(_) { SelectPosition(Ephemeral) }),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
],
|
||||||
])
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub fn view(s: Settings, image_id: String, html_id: String, class: String) {
|
||||||
[
|
[
|
||||||
img([
|
img([
|
||||||
attribute.id(html_id),
|
attribute.id(html_id),
|
||||||
attribute.alt(""),
|
attribute.attribute("aria-hidden", "true"),
|
||||||
attribute.src(artwork.url(s.artwork_server, image_id, artwork.S300)),
|
attribute.src(artwork.url(s.artwork_server, image_id, artwork.S300)),
|
||||||
attribute.attribute("loading", "lazy"),
|
attribute.attribute("loading", "lazy"),
|
||||||
attribute.width(300),
|
attribute.width(300),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { preventPopstate } from "./browser_ffi.mjs";
|
||||||
|
|
||||||
const player_id = "player-elem";
|
const player_id = "player-elem";
|
||||||
const track_id = "player-track";
|
const track_id = "player-track";
|
||||||
|
|
||||||
|
@ -11,6 +13,21 @@ let player;
|
||||||
*/
|
*/
|
||||||
let track;
|
let track;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
let popstatePreventEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
let popstateListenerAdded = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {(function(): undefined) | null}
|
||||||
|
*/
|
||||||
|
let popstateListenerCallback = null;
|
||||||
|
|
||||||
export function registerCallback(event, callback) {
|
export function registerCallback(event, callback) {
|
||||||
getPlayer();
|
getPlayer();
|
||||||
player.addEventListener(event, callback);
|
player.addEventListener(event, callback);
|
||||||
|
@ -68,6 +85,31 @@ export function linearRampToValue(node, gain, at) {
|
||||||
node.gain.linearRampToValueAtTime(gain, at);
|
node.gain.linearRampToValueAtTime(gain, at);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function enablePreventPopstate(callback) {
|
||||||
|
if (!popstateListenerAdded) {
|
||||||
|
window.addEventListener("popstate", popstateListener);
|
||||||
|
popstateListenerAdded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
popstatePreventEnabled = true;
|
||||||
|
popstateListenerCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function disablePreventPopstate() {
|
||||||
|
popstatePreventEnabled = false;
|
||||||
|
popstateListenerCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popstateListener() {
|
||||||
|
if (popstatePreventEnabled) {
|
||||||
|
preventPopstate();
|
||||||
|
|
||||||
|
if (popstateListenerCallback) {
|
||||||
|
popstateListenerCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getPlayer() {
|
function getPlayer() {
|
||||||
if (player === undefined) {
|
if (player === undefined) {
|
||||||
player = document.getElementById(player_id);
|
player = document.getElementById(player_id);
|
||||||
|
|
54
style.css
54
style.css
|
@ -167,27 +167,25 @@ single-album-view {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#authed-view-player {
|
#authed-view-wrapper[data-player-status="closed"] .player-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#small-player-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--side-margin);
|
padding: var(--side-margin);
|
||||||
|
|
||||||
border-radius: 10px 10px 0 0;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#authed-view-wrapper[data-player-status="closed"] #authed-view-player {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#small-player-wrapper {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template: "art track" "art artist" "art album" "controls controls" auto / 2fr 8fr;
|
grid-template: "art track" "art artist" "art album" "controls controls" auto / 2fr 8fr;
|
||||||
gap: var(--side-margin);
|
gap: var(--side-margin);
|
||||||
|
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
|
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#small-player-track-name,
|
#small-player-track-name,
|
||||||
|
@ -198,24 +196,52 @@ single-album-view {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#small-player-track-name {
|
#small-player-track-name,
|
||||||
|
#full-player-track-name {
|
||||||
grid-area: track;
|
grid-area: track;
|
||||||
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#small-player-artist-name {
|
#small-player-artist-name,
|
||||||
|
#full-player-artist-name {
|
||||||
grid-area: artist;
|
grid-area: artist;
|
||||||
}
|
}
|
||||||
|
|
||||||
#small-player-album-name {
|
#small-player-album-name,
|
||||||
|
#full-player-album-name {
|
||||||
grid-area: album;
|
grid-area: album;
|
||||||
}
|
}
|
||||||
|
|
||||||
#small-player-artwork-wrapper {
|
#small-player-artwork-wrapper,
|
||||||
|
#full-player-artwork-wrapper {
|
||||||
grid-area: art;
|
grid-area: art;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#full-player-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: var(--double-margin);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template: "art art" "track track" "artist artist" "album album" ". ." 1fr "controls controls" auto / 1fr 1fr;
|
||||||
|
gap: var(--side-margin);
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 100;
|
||||||
|
font-size: 125%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#full-player-artwork-wrapper img {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-bottom: var(--side-margin);
|
||||||
|
}
|
||||||
|
|
||||||
#player-controls {
|
#player-controls {
|
||||||
grid-area: controls;
|
grid-area: controls;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue