Track list is rendered
This commit is contained in:
parent
09f85315e4
commit
2672078e2c
8 changed files with 96 additions and 113 deletions
3
src/custom_event_ffi.mjs
Normal file
3
src/custom_event_ffi.mjs
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function getDetail(e) {
|
||||
return e.detail;
|
||||
}
|
4
src/elekf/utils/custom_event.gleam
Normal file
4
src/elekf/utils/custom_event.gleam
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub type CustomEvent
|
||||
|
||||
@external(javascript, "../../custom_event_ffi.mjs", "getDetail")
|
||||
pub fn get_detail(e: CustomEvent) -> a
|
|
@ -13,7 +13,7 @@ import lustre/element/html.{div, h3, p}
|
|||
import lustre/attribute
|
||||
import lustre/event
|
||||
import lustre/effect
|
||||
import elekf/web/common
|
||||
import elekf/web/common.{PlayQueue}
|
||||
import ibroadcast/library/library as library_api
|
||||
import ibroadcast/authed_request.{RequestConfig}
|
||||
import elekf/api/base_request_config.{base_request_config}
|
||||
|
@ -24,11 +24,9 @@ import elekf/transfer/library as library_transfer
|
|||
import elekf/web/components/player
|
||||
import elekf/web/components/search
|
||||
import elekf/web/components/library_views/tracks_view
|
||||
import elekf/web/events/start_play
|
||||
import elekf/web/utils
|
||||
|
||||
pub type PlayQueue =
|
||||
List(#(Int, Track))
|
||||
|
||||
pub type PlayInfo {
|
||||
PlayInfo(
|
||||
track_id: Int,
|
||||
|
@ -61,7 +59,6 @@ pub type Msg {
|
|||
PlayerMsg(player.Msg)
|
||||
Search(search.Msg)
|
||||
StartPlay(PlayQueue, Int)
|
||||
ShuffleAll
|
||||
}
|
||||
|
||||
pub fn init(auth_data: common.AuthData) {
|
||||
|
@ -119,6 +116,7 @@ pub fn update(model: Model, msg) {
|
|||
#(
|
||||
Model(
|
||||
..model,
|
||||
loading_library: False,
|
||||
library: library_transfer.from(data.library),
|
||||
settings: option.Some(settings),
|
||||
),
|
||||
|
@ -128,10 +126,10 @@ pub fn update(model: Model, msg) {
|
|||
LibraryResult(Error(error)) -> {
|
||||
io.println_error("Library load failed:")
|
||||
io.debug(error)
|
||||
#(model, effect.none())
|
||||
#(Model(..model, loading_library: False), effect.none())
|
||||
}
|
||||
StartPlay(tracks, position) -> {
|
||||
let #(status, effect) = start_play(model, tracks, position)
|
||||
let #(status, effect) = handle_start_play(model, tracks, position)
|
||||
#(Model(..model, play_status: status), effect)
|
||||
}
|
||||
PlayerMsg(player.NextTrack) -> {
|
||||
|
@ -142,7 +140,7 @@ pub fn update(model: Model, msg) {
|
|||
case list.at(info.play_queue, next_index) {
|
||||
Ok(_) -> {
|
||||
let #(status, effect) =
|
||||
start_play(model, info.play_queue, next_index)
|
||||
handle_start_play(model, info.play_queue, next_index)
|
||||
#(Model(..model, play_status: status), effect)
|
||||
}
|
||||
Error(_) -> #(model, effect.none())
|
||||
|
@ -172,23 +170,6 @@ pub fn update(model: Model, msg) {
|
|||
|
||||
#(Model(..model, search: search_model), effect.none())
|
||||
}
|
||||
ShuffleAll -> {
|
||||
let tracks =
|
||||
model.library.tracks
|
||||
|> map.to_list()
|
||||
|> list.shuffle()
|
||||
|
||||
let #(model, effect) = case list.length(tracks) > 0 {
|
||||
True -> {
|
||||
let #(play_status, e) = start_play(model, tracks, 0)
|
||||
|
||||
#(Model(..model, play_status: play_status), e)
|
||||
}
|
||||
False -> #(model, effect.none())
|
||||
}
|
||||
|
||||
#(model, effect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,60 +181,12 @@ pub fn view(model: Model) {
|
|||
[
|
||||
case model.loading_library {
|
||||
True -> p([], [text("Loading library…")])
|
||||
False -> div([], [tracks_view.render(model.library)])
|
||||
False -> text("")
|
||||
},
|
||||
// div(
|
||||
// [attribute.class("track-list")],
|
||||
// list.append(
|
||||
// [
|
||||
// div(
|
||||
// [
|
||||
// attribute.class("track-list-shuffle-all"),
|
||||
// event.on_click(ShuffleAll),
|
||||
// ],
|
||||
// [h3([attribute.class("track-title")], [text("Shuffle all")])],
|
||||
// ),
|
||||
// ],
|
||||
// {
|
||||
// let tracks =
|
||||
// map.to_list(lib.tracks)
|
||||
// |> list.filter(fn(track) {
|
||||
// search_text == "" || string.contains(
|
||||
// { track.1 }.title_lower,
|
||||
// search_text,
|
||||
// )
|
||||
// })
|
||||
|
||||
// list.index_map(
|
||||
// tracks,
|
||||
// fn(i, item) {
|
||||
// let #(track_id, track) = item
|
||||
// let album = library.assert_album(lib, track.album_id)
|
||||
// let artist = library.assert_artist(lib, track.artist_id)
|
||||
// div(
|
||||
// [
|
||||
// attribute.id("track-list-" <> int.to_string(track_id)),
|
||||
// attribute.type_("button"),
|
||||
// event.on_click(StartPlay(tracks, i)),
|
||||
// attribute.attribute("role", "button"),
|
||||
// ],
|
||||
// [
|
||||
// h3(
|
||||
// [attribute.class("track-title")],
|
||||
// [text(track.title)],
|
||||
// ),
|
||||
// p(
|
||||
// [attribute.class("track-artist")],
|
||||
// [text(artist.name)],
|
||||
// ),
|
||||
// p([attribute.class("track-album")], [text(album.name)]),
|
||||
// ],
|
||||
// )
|
||||
// },
|
||||
// )
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
tracks_view.render(
|
||||
model.library,
|
||||
[attribute.id("tracks-view"), start_play.on(StartPlay)],
|
||||
),
|
||||
div(
|
||||
[attribute.id("search-positioner")],
|
||||
[
|
||||
|
@ -276,7 +209,7 @@ pub fn view(model: Model) {
|
|||
)
|
||||
}
|
||||
|
||||
fn start_play(model: Model, queue: PlayQueue, position: Int) {
|
||||
fn handle_start_play(model: Model, queue: PlayQueue, position: Int) {
|
||||
let assert Ok(#(track_id, track)) = list.at(queue, position)
|
||||
|
||||
let player_model = case model.play_status {
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import elekf/library/track.{Track}
|
||||
import elekf/api/auth/models as auth_models
|
||||
|
||||
/// A queue of tracks to play, with their IDs.
|
||||
pub type PlayQueue =
|
||||
List(#(Int, Track))
|
||||
|
||||
/// Authentication data for the user.
|
||||
pub type AuthData {
|
||||
AuthData(user: auth_models.User, device: auth_models.Device)
|
||||
|
|
|
@ -59,12 +59,20 @@ pub type Model(a) {
|
|||
Model(
|
||||
library: Library,
|
||||
data: List(LibraryItem(a)),
|
||||
item_view: ItemView(a),
|
||||
data_getter: DataGetter(a),
|
||||
shuffler: Shuffler(a),
|
||||
)
|
||||
}
|
||||
|
||||
/// Register the component as a custom element in the app.
|
||||
///
|
||||
/// This must be implemented by the client component, passing suitable values.
|
||||
///
|
||||
/// `data_getter` must be a callback that gets the library and must return the
|
||||
/// items to show.
|
||||
/// `item_view` is the renderer for individual items.
|
||||
/// `shuffler` is the function used to find all the tracks in the current view
|
||||
/// and shuffle them for play.
|
||||
pub fn register(
|
||||
name: String,
|
||||
data_getter: DataGetter(a),
|
||||
|
@ -73,26 +81,34 @@ pub fn register(
|
|||
) {
|
||||
lustre.component(
|
||||
name,
|
||||
fn() { init(library.empty(), data_getter, item_view, shuffler) },
|
||||
fn() { init(library.empty(), data_getter, shuffler) },
|
||||
update,
|
||||
view,
|
||||
generate_view(item_view),
|
||||
map.from_list([#("library", library_decode)]),
|
||||
)
|
||||
}
|
||||
|
||||
/// Render the component using a custom element.
|
||||
pub fn render(name: String, library: Library) {
|
||||
element.element(name, [attribute.property("library", library)], [])
|
||||
pub fn render(
|
||||
name: String,
|
||||
library: Library,
|
||||
extra_attrs: List(attribute.Attribute(msg)),
|
||||
) {
|
||||
element.element(
|
||||
name,
|
||||
list.concat([[attribute.property("library", library)], extra_attrs]),
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
fn init(library, data_getter, item_view, shuffler) {
|
||||
#(Model(library, data_getter(library), item_view, shuffler), effect.none())
|
||||
fn init(library, data_getter, shuffler) {
|
||||
#(Model(library, data_getter(library), data_getter, shuffler), effect.none())
|
||||
}
|
||||
|
||||
fn update(model, msg) {
|
||||
case msg {
|
||||
LibraryUpdated(library) -> #(
|
||||
Model(..model, library: library),
|
||||
Model(..model, library: library, data: model.data_getter(library)),
|
||||
effect.none(),
|
||||
)
|
||||
ShuffleAll -> #(model, shuffle_all(model))
|
||||
|
@ -100,7 +116,8 @@ fn update(model, msg) {
|
|||
}
|
||||
}
|
||||
|
||||
fn view(model: Model(a)) {
|
||||
fn generate_view(item_view: ItemView(a)) {
|
||||
fn(model: Model(a)) {
|
||||
div(
|
||||
[attribute.class("library-list")],
|
||||
list.append(
|
||||
|
@ -115,10 +132,11 @@ fn view(model: Model(a)) {
|
|||
],
|
||||
list.index_map(
|
||||
model.data,
|
||||
fn(i, item) { model.item_view(model.library, model.data, i, item) },
|
||||
fn(i, item) { item_view(model.library, model.data, i, item) },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn shuffle_all(model: Model(a)) {
|
||||
|
|
|
@ -19,8 +19,8 @@ pub fn register() {
|
|||
}
|
||||
|
||||
/// Render the tracks view.
|
||||
pub fn render(library: Library) {
|
||||
library_view.render(component_name, library)
|
||||
pub fn render(library: Library, extra_attrs: List(attribute.Attribute(msg))) {
|
||||
library_view.render(component_name, library, extra_attrs)
|
||||
}
|
||||
|
||||
fn data_getter(library: Library) {
|
||||
|
|
|
@ -1,17 +1,37 @@
|
|||
import gleam/result
|
||||
import gleam/dynamic
|
||||
import lustre/event
|
||||
import elekf/library/track.{Track}
|
||||
import elekf/web/common.{PlayQueue}
|
||||
import elekf/utils/custom_event.{CustomEvent}
|
||||
|
||||
pub const event_name = "start-play"
|
||||
const event_name = "start-play"
|
||||
|
||||
pub type EventData {
|
||||
EventData(tracks: List(#(Int, Track)), position: Int)
|
||||
EventData(tracks: PlayQueue, position: Int)
|
||||
}
|
||||
|
||||
pub fn emit(tracks: List(#(Int, Track)), position: Int) {
|
||||
pub fn emit(tracks: PlayQueue, position: Int) {
|
||||
event.emit(event_name, EventData(tracks, position))
|
||||
}
|
||||
|
||||
pub fn decoder(data) -> EventData {
|
||||
dynamic.unsafe_coerce(data)
|
||||
pub fn on(msg: fn(PlayQueue, Int) -> b) {
|
||||
event.on(
|
||||
event_name,
|
||||
fn(data) {
|
||||
data
|
||||
|> decoder
|
||||
|> result.map(fn(e) { msg(e.tracks, e.position) })
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn decoder(data: dynamic.Dynamic) {
|
||||
let e: CustomEvent = dynamic.unsafe_coerce(data)
|
||||
let detail = custom_event.get_detail(e)
|
||||
let event_data: EventData =
|
||||
detail
|
||||
|> dynamic.from()
|
||||
|> dynamic.unsafe_coerce()
|
||||
|
||||
Ok(event_data)
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ main {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.track-list {
|
||||
tracks-view {
|
||||
flex: 1 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue