Make track fully controlled
This commit is contained in:
parent
d067370071
commit
4b9c81d24c
5 changed files with 67 additions and 88 deletions
|
@ -148,10 +148,9 @@ pub fn update(model: Model, msg) {
|
||||||
effect.none(),
|
effect.none(),
|
||||||
)
|
)
|
||||||
actions.SelectPosition(actions.Commit) -> {
|
actions.SelectPosition(actions.Commit) -> {
|
||||||
let pos = current_track_position()
|
skip_to_time(model.position)
|
||||||
skip_to_time(pos)
|
|
||||||
|
|
||||||
#(Model(..model, position: pos), effect.none())
|
#(model, effect.none())
|
||||||
}
|
}
|
||||||
actions.SelectPosition(actions.Ephemeral) -> {
|
actions.SelectPosition(actions.Ephemeral) -> {
|
||||||
let pos = current_track_position()
|
let pos = current_track_position()
|
||||||
|
@ -193,7 +192,6 @@ pub fn update(model: Model, msg) {
|
||||||
True -> #(model, effect.none())
|
True -> #(model, effect.none())
|
||||||
False -> {
|
False -> {
|
||||||
let pos = float.truncate(time)
|
let pos = float.truncate(time)
|
||||||
set_track_to(pos)
|
|
||||||
|
|
||||||
case
|
case
|
||||||
position.make_position_state(
|
position.make_position_state(
|
||||||
|
@ -361,9 +359,6 @@ fn skip_to_time(time: Int) -> Nil
|
||||||
@external(javascript, "../../../player_ffi.mjs", "currentTrackPosition")
|
@external(javascript, "../../../player_ffi.mjs", "currentTrackPosition")
|
||||||
fn current_track_position() -> Int
|
fn current_track_position() -> Int
|
||||||
|
|
||||||
@external(javascript, "../../../player_ffi.mjs", "setTrackTo")
|
|
||||||
fn set_track_to(pos: Int) -> Nil
|
|
||||||
|
|
||||||
@external(javascript, "../../../player_ffi.mjs", "createAudioContext")
|
@external(javascript, "../../../player_ffi.mjs", "createAudioContext")
|
||||||
fn create_audio_context() -> AudioContext
|
fn create_audio_context() -> AudioContext
|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,21 @@ import gleam/int
|
||||||
import gleam/float
|
import gleam/float
|
||||||
import gleam/option
|
import gleam/option
|
||||||
import lustre/element.{text}
|
import lustre/element.{text}
|
||||||
import lustre/element/html.{div, input, p}
|
import lustre/element/html.{div, p}
|
||||||
import lustre/attribute
|
import lustre/attribute
|
||||||
import lustre/event
|
import lustre/event
|
||||||
import elekf/utils/lustre
|
import elekf/utils/lustre
|
||||||
import elekf/web/components/icon.{Alt, icon}
|
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_group
|
||||||
import elekf/web/components/button
|
import elekf/web/components/button
|
||||||
import elekf/web/components/link
|
import elekf/web/components/link
|
||||||
import elekf/web/components/thumbnail
|
import elekf/web/components/thumbnail
|
||||||
import elekf/web/components/player/actions.{
|
import elekf/web/components/player/actions.{NextTrack, Pause, Play, PrevTrack}
|
||||||
Commit, EndUserSkip, Ephemeral, NextTrack, Pause, Play, PrevTrack,
|
|
||||||
SelectPosition, StartUserSkip,
|
|
||||||
}
|
|
||||||
import elekf/web/components/player/model.{type Model}
|
import elekf/web/components/player/model.{type Model}
|
||||||
|
import elekf/web/components/player/track
|
||||||
|
|
||||||
pub fn view(model: Model) {
|
pub fn view(model: Model) {
|
||||||
let is_playing = model.state == model.Playing
|
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(
|
div(
|
||||||
[
|
[
|
||||||
|
@ -96,33 +89,7 @@ pub fn view(model: Model) {
|
||||||
[icon("arrows-angle-contract", Alt("Small player"))],
|
[icon("arrows-angle-contract", Alt("Small player"))],
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
p(
|
..track.view(model)
|
||||||
[
|
|
||||||
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,28 +1,20 @@
|
||||||
import gleam/int
|
|
||||||
import gleam/option
|
import gleam/option
|
||||||
import lustre/element.{text}
|
import lustre/element.{text}
|
||||||
import lustre/element/html.{div, input, p}
|
import lustre/element/html.{div, p}
|
||||||
import lustre/attribute
|
import lustre/attribute
|
||||||
import lustre/event
|
import lustre/event
|
||||||
import elekf/web/components/icon.{Alt, icon}
|
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_group
|
||||||
import elekf/web/components/button
|
import elekf/web/components/button
|
||||||
import elekf/web/components/link
|
import elekf/web/components/link
|
||||||
import elekf/web/components/thumbnail
|
import elekf/web/components/thumbnail
|
||||||
import elekf/web/components/player/actions.{
|
import elekf/web/components/player/actions.{NextTrack, Pause, Play, PrevTrack}
|
||||||
Commit, EndUserSkip, Ephemeral, NextTrack, Pause, Play, PrevTrack,
|
|
||||||
SelectPosition, StartUserSkip,
|
|
||||||
}
|
|
||||||
import elekf/web/components/player/model.{type Model}
|
import elekf/web/components/player/model.{type Model}
|
||||||
|
import elekf/web/components/player/track
|
||||||
import elekf/web/router
|
import elekf/web/router
|
||||||
|
|
||||||
pub fn view(model: Model) {
|
pub fn view(model: Model) {
|
||||||
let is_playing = model.state == model.Playing
|
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(
|
div(
|
||||||
[
|
[
|
||||||
|
@ -78,33 +70,7 @@ pub fn view(model: Model) {
|
||||||
[icon("arrows-angle-expand", Alt("Full screen player"))],
|
[icon("arrows-angle-expand", Alt("Full screen player"))],
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
p(
|
..track.view(model)
|
||||||
[
|
|
||||||
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) }),
|
|
||||||
]),
|
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
55
src/elekf/web/components/player/track.gleam
Normal file
55
src/elekf/web/components/player/track.gleam
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
//// A track for visualising the current song's progress and skipping it back
|
||||||
|
//// and forth, and additional timestamps.
|
||||||
|
|
||||||
|
import gleam/int
|
||||||
|
import gleam/dynamic
|
||||||
|
import lustre/element/html.{input, p}
|
||||||
|
import lustre/attribute
|
||||||
|
import lustre/event
|
||||||
|
import elekf/web/components/track_length.{track_length}
|
||||||
|
import elekf/web/components/player/model
|
||||||
|
import elekf/web/components/player/actions.{
|
||||||
|
Commit, EndUserSkip, Ephemeral, SelectPosition, StartUserSkip,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view(model: model.Model) {
|
||||||
|
let current_time_padding = case model.track.length > 3600 {
|
||||||
|
True -> track_length.Hours
|
||||||
|
False -> track_length.Auto
|
||||||
|
}
|
||||||
|
|
||||||
|
let track_pos = dynamic.from(int.to_string(model.position))
|
||||||
|
|
||||||
|
[
|
||||||
|
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.value(track_pos),
|
||||||
|
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("touchstart", fn(_) { Ok(StartUserSkip) }),
|
||||||
|
event.on("touchend", fn(_) { Ok(EndUserSkip) }),
|
||||||
|
event.on_input(fn(_) { SelectPosition(Ephemeral) }),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
}
|
|
@ -41,11 +41,6 @@ export function currentTrackPosition() {
|
||||||
return track.valueAsNumber;
|
return track.valueAsNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setTrackTo(pos) {
|
|
||||||
getTrack();
|
|
||||||
track.value = String(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createAudioContext() {
|
export function createAudioContext() {
|
||||||
return new globalThis.AudioContext();
|
return new globalThis.AudioContext();
|
||||||
}
|
}
|
||||||
|
@ -75,7 +70,8 @@ function getPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTrack() {
|
function getTrack() {
|
||||||
if (track === undefined) {
|
// Track element might not be fetched yet, or it might have been removed from the DOM
|
||||||
|
if (track === undefined || !track.isConnected) {
|
||||||
track = document.getElementById(track_id);
|
track = document.getElementById(track_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue