Beginnings of refactoring to component based model
This commit is contained in:
parent
f8310f11e6
commit
8ea17ecd82
6 changed files with 256 additions and 53 deletions
|
@ -13,6 +13,11 @@ pub type Library {
|
|||
)
|
||||
}
|
||||
|
||||
/// Gets an empty library for use as a default value.
|
||||
pub fn empty() {
|
||||
Library(albums: map.new(), artists: map.new(), tracks: map.new())
|
||||
}
|
||||
|
||||
/// Gets an album from the library based on ID.
|
||||
pub fn get_album(library: Library, id: Int) {
|
||||
map.get(library.albums, id)
|
||||
|
|
|
@ -23,6 +23,7 @@ import elekf/library/track.{Track}
|
|||
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/utils
|
||||
|
||||
pub type PlayQueue =
|
||||
|
@ -201,60 +202,60 @@ pub fn view(model: Model) {
|
|||
[
|
||||
case model.library {
|
||||
option.None -> p([], [text("Loading library…")])
|
||||
option.Some(lib) ->
|
||||
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)]),
|
||||
],
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
option.Some(lib) -> tracks_view.render(lib)
|
||||
},
|
||||
// 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)]),
|
||||
// ],
|
||||
// )
|
||||
// },
|
||||
// )
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
div(
|
||||
[attribute.id("search-positioner")],
|
||||
[
|
||||
|
|
125
src/elekf/web/components/library_view.gleam
Normal file
125
src/elekf/web/components/library_view.gleam
Normal file
|
@ -0,0 +1,125 @@
|
|||
//// A view to the library, presenting artists, albums, or tracks.
|
||||
|
||||
import gleam/dynamic
|
||||
import gleam/list
|
||||
import gleam/map
|
||||
import lustre
|
||||
import lustre/effect
|
||||
import lustre/element.{text}
|
||||
import lustre/element/html.{div, h3}
|
||||
import lustre/attribute
|
||||
import lustre/event
|
||||
import elekf/library.{Library}
|
||||
import elekf/library/album.{Album}
|
||||
import elekf/library/artist.{Artist}
|
||||
import elekf/library/track.{Track}
|
||||
import elekf/web/events/start_play
|
||||
|
||||
pub const start_play_event = "start-play"
|
||||
|
||||
pub type LibraryItem(a) =
|
||||
#(Int, a)
|
||||
|
||||
pub type DataGetter(a) =
|
||||
fn(Library) -> List(LibraryItem(a))
|
||||
|
||||
pub type ItemView(a) =
|
||||
fn(Library, List(LibraryItem(a)), Int, LibraryItem(a)) -> element.Element(Msg)
|
||||
|
||||
pub type Shuffler(a) =
|
||||
fn(List(LibraryItem(a))) -> List(LibraryItem(Track))
|
||||
|
||||
pub type View {
|
||||
/// All albums.
|
||||
Albums
|
||||
/// Tracks of a single album.
|
||||
SingleAlbum(Int, Album)
|
||||
/// All artists.
|
||||
Artists
|
||||
/// Albums of a single artist.
|
||||
SingleArtist(Int, Artist)
|
||||
/// Tracks of a single artist.
|
||||
SingleArtistTracks(Int, Artist)
|
||||
/// All tracks.
|
||||
Tracks
|
||||
}
|
||||
|
||||
pub type Msg {
|
||||
LibraryUpdated(Library)
|
||||
ShuffleAll
|
||||
StartPlay(List(LibraryItem(Track)), Int)
|
||||
}
|
||||
|
||||
pub type Model(a) {
|
||||
Model(
|
||||
library: Library,
|
||||
data: List(LibraryItem(a)),
|
||||
item_view: ItemView(a),
|
||||
shuffler: Shuffler(a),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
name: String,
|
||||
data_getter: DataGetter(a),
|
||||
item_view: ItemView(a),
|
||||
shuffler: Shuffler(a),
|
||||
) {
|
||||
lustre.component(
|
||||
name,
|
||||
fn() { init(library.empty(), data_getter, item_view, shuffler) },
|
||||
update,
|
||||
view,
|
||||
map.from_list([#("library", library_decode)]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render(name: String, library: Library) {
|
||||
element.element(name, [attribute.property("library", library)], [])
|
||||
}
|
||||
|
||||
fn init(library, data_getter, item_view, shuffler) {
|
||||
#(Model(library, data_getter(library), item_view, shuffler), effect.none())
|
||||
}
|
||||
|
||||
fn update(model, msg) {
|
||||
case msg {
|
||||
LibraryUpdated(library) -> #(
|
||||
Model(..model, library: library),
|
||||
effect.none(),
|
||||
)
|
||||
ShuffleAll -> #(model, shuffle_all(model))
|
||||
StartPlay(tracks, position) -> #(model, start_play.emit(tracks, position))
|
||||
}
|
||||
}
|
||||
|
||||
fn view(model: Model(a)) {
|
||||
div(
|
||||
[attribute.class("library-list")],
|
||||
list.append(
|
||||
[
|
||||
div(
|
||||
[
|
||||
attribute.class("library-item library-list-shuffle-all"),
|
||||
event.on_click(ShuffleAll),
|
||||
],
|
||||
[h3([attribute.class("library-item-title")], [text("Shuffle all")])],
|
||||
),
|
||||
],
|
||||
list.index_map(
|
||||
model.data,
|
||||
fn(i, item) { model.item_view(model.library, model.data, i, item) },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn shuffle_all(model: Model(a)) {
|
||||
let tracks = model.shuffler(model.data)
|
||||
start_play.emit(tracks, 0)
|
||||
}
|
||||
|
||||
fn library_decode(data: dynamic.Dynamic) {
|
||||
let library: Library = dynamic.unsafe_coerce(data)
|
||||
Ok(LibraryUpdated(library))
|
||||
}
|
52
src/elekf/web/components/library_views/tracks_view.gleam
Normal file
52
src/elekf/web/components/library_views/tracks_view.gleam
Normal file
|
@ -0,0 +1,52 @@
|
|||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/map
|
||||
import lustre/element/html.{div, h3, p}
|
||||
import lustre/element.{text}
|
||||
import lustre/attribute
|
||||
import lustre/event
|
||||
import elekf/library.{Library}
|
||||
import elekf/library/track.{Track}
|
||||
import elekf/web/components/library_view.{LibraryItem, StartPlay}
|
||||
|
||||
const component_name = "tracks-view"
|
||||
|
||||
pub fn register() {
|
||||
library_view.register(component_name, data_getter, item_view, shuffler)
|
||||
}
|
||||
|
||||
pub fn render(library: Library) {
|
||||
library_view.render(component_name, library)
|
||||
}
|
||||
|
||||
fn data_getter(library: Library) {
|
||||
map.to_list(library.tracks)
|
||||
}
|
||||
|
||||
fn shuffler(items) {
|
||||
list.shuffle(items)
|
||||
}
|
||||
|
||||
fn item_view(
|
||||
library: Library,
|
||||
items: List(LibraryItem(Track)),
|
||||
index: Int,
|
||||
item: LibraryItem(Track),
|
||||
) {
|
||||
let #(track_id, track) = item
|
||||
let album = library.assert_album(library, track.album_id)
|
||||
let artist = library.assert_artist(library, track.artist_id)
|
||||
div(
|
||||
[
|
||||
attribute.id("track-list-" <> int.to_string(track_id)),
|
||||
attribute.type_("button"),
|
||||
event.on_click(StartPlay(items, index)),
|
||||
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)]),
|
||||
],
|
||||
)
|
||||
}
|
17
src/elekf/web/events/start_play.gleam
Normal file
17
src/elekf/web/events/start_play.gleam
Normal file
|
@ -0,0 +1,17 @@
|
|||
import gleam/dynamic
|
||||
import lustre/event
|
||||
import elekf/library/track.{Track}
|
||||
|
||||
pub const event_name = "start-play"
|
||||
|
||||
pub type EventData {
|
||||
EventData(tracks: List(#(Int, Track)), position: Int)
|
||||
}
|
||||
|
||||
pub fn emit(tracks: List(#(Int, Track)), position: Int) {
|
||||
event.emit(event_name, EventData(tracks, position))
|
||||
}
|
||||
|
||||
pub fn decoder(data) -> EventData {
|
||||
dynamic.unsafe_coerce(data)
|
||||
}
|
|
@ -12,6 +12,7 @@ import elekf/web/login_view
|
|||
import elekf/web/authed_view
|
||||
import elekf/web/view
|
||||
import elekf/web/utils
|
||||
import elekf/web/components/library_views/tracks_view
|
||||
import elekf/api/auth/storage as auth_storage
|
||||
import elekf/api/auth/models as auth_models
|
||||
import elekf/utils/option as option_utils
|
||||
|
@ -33,6 +34,8 @@ type Msg {
|
|||
}
|
||||
|
||||
pub fn main() {
|
||||
let assert Ok(_) = tracks_view.register()
|
||||
|
||||
let app = lustre.application(init, update, view)
|
||||
let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil)
|
||||
|
||||
|
|
Loading…
Reference in a new issue