This commit is contained in:
Mikko Ahlroth 2024-02-27 12:06:22 +02:00
parent 830918c344
commit 773627bd5d
7 changed files with 42 additions and 149 deletions

View file

@ -1,15 +1,15 @@
//// The library stores the music library retrieved from iBroadcast. //// The library stores the music library retrieved from iBroadcast.
import gleam/map.{type Map} import gleam/dict.{type Dict}
import elekf/library/track.{type Track} import elekf/library/track.{type Track}
import elekf/library/album.{type Album} import elekf/library/album.{type Album}
import elekf/library/artist.{type Artist} import elekf/library/artist.{type Artist}
pub type Library { pub type Library {
Library( Library(
albums: Map(Int, Album), albums: Dict(Int, Album),
artists: Map(Int, Artist), artists: Dict(Int, Artist),
tracks: Map(Int, Track), tracks: Dict(Int, Track),
) )
} }
@ -18,38 +18,38 @@ pub const invalid_id = -1
/// Gets an empty library for use as a default value. /// Gets an empty library for use as a default value.
pub fn empty() { pub fn empty() {
Library(albums: map.new(), artists: map.new(), tracks: map.new()) Library(albums: dict.new(), artists: dict.new(), tracks: dict.new())
} }
/// Gets an album from the library based on ID. /// Gets an album from the library based on ID.
pub fn get_album(library: Library, id: Int) { pub fn get_album(library: Library, id: Int) {
map.get(library.albums, id) dict.get(library.albums, id)
} }
/// Gets an artist from the library based on ID. /// Gets an artist from the library based on ID.
pub fn get_artist(library: Library, id: Int) { pub fn get_artist(library: Library, id: Int) {
map.get(library.artists, id) dict.get(library.artists, id)
} }
/// Gets a track from the library based on ID. /// Gets a track from the library based on ID.
pub fn get_track(library: Library, id: Int) { pub fn get_track(library: Library, id: Int) {
map.get(library.tracks, id) dict.get(library.tracks, id)
} }
/// Gets an album from the library, asserting that it exists. /// Gets an album from the library, asserting that it exists.
pub fn assert_album(library: Library, id: Int) { pub fn assert_album(library: Library, id: Int) {
let assert Ok(album) = map.get(library.albums, id) let assert Ok(album) = dict.get(library.albums, id)
album album
} }
/// Gets an artist from the library, asserting that it exists. /// Gets an artist from the library, asserting that it exists.
pub fn assert_artist(library: Library, id: Int) { pub fn assert_artist(library: Library, id: Int) {
let assert Ok(artist) = map.get(library.artists, id) let assert Ok(artist) = dict.get(library.artists, id)
artist artist
} }
/// Gets a track from the library, asserting that it exists. /// Gets a track from the library, asserting that it exists.
pub fn assert_track(library: Library, id: Int) { pub fn assert_track(library: Library, id: Int) {
let assert Ok(track) = map.get(library.tracks, id) let assert Ok(track) = dict.get(library.tracks, id)
track track
} }

View file

@ -16,10 +16,7 @@ import lustre/event
import elekf/utils/order.{type Sorter} import elekf/utils/order.{type Sorter}
import elekf/library.{type Library} import elekf/library.{type Library}
import elekf/library/track.{type Track} import elekf/library/track.{type Track}
import elekf/library/artist.{type Artist}
import elekf/library/album.{type Album}
import elekf/web/events/start_play import elekf/web/events/start_play
import elekf/web/events/show_artist
import elekf/web/components/search import elekf/web/components/search
import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_item.{type LibraryItem}
import elekf/web/components/shuffle_all import elekf/web/components/shuffle_all
@ -70,9 +67,7 @@ pub type Msg {
SettingsUpdated(option.Option(common.Settings)) SettingsUpdated(option.Option(common.Settings))
ShuffleAll ShuffleAll
StartPlay(List(LibraryItem(Track)), Int) StartPlay(List(LibraryItem(Track)), Int)
ShowArtist(LibraryItem(Artist))
Search(search.Msg) Search(search.Msg)
AlbumExpandToggled(LibraryItem(Album))
FilterUpdated FilterUpdated
} }
@ -168,15 +163,11 @@ pub fn update(model, msg) {
) )
ShuffleAll -> #(model, shuffle_all(model)) ShuffleAll -> #(model, shuffle_all(model))
StartPlay(tracks, position) -> #(model, start_play.emit(tracks, position)) StartPlay(tracks, position) -> #(model, start_play.emit(tracks, position))
ShowArtist(artist) -> #(model, show_artist.emit(artist))
Search(search_msg) -> { Search(search_msg) -> {
let search_model = search.update(model.search, search_msg) let search_model = search.update(model.search, search_msg)
#(Model(..model, search: search_model), effect.none()) #(Model(..model, search: search_model), effect.none())
} }
// Base case, this should be handled in an implementing view
AlbumExpandToggled(_album) -> #(model, effect.none())
FilterUpdated -> #(update_data(model, model.library), effect.none()) FilterUpdated -> #(update_data(model, model.library), effect.none())
} }
} }

View file

@ -3,20 +3,14 @@
import gleam/list import gleam/list
import gleam/option import gleam/option
import gleam/int import gleam/int
import lustre/element/html.{div, h3, p} import lustre/element/html.{h3, p}
import lustre/element.{text} import lustre/element.{text}
import lustre/attribute import lustre/attribute
import lustre/event
import elekf/library.{type Library} import elekf/library.{type Library}
import elekf/library/album.{type Album} import elekf/library/album.{type Album}
import elekf/library/track.{type Track}
import elekf/library/album_utils import elekf/library/album_utils
import elekf/library/track_utils
import elekf/web/components/library_view.{AlbumExpandToggled, ShuffleAll}
import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_item.{type LibraryItem}
import elekf/web/components/library_views/track_item
import elekf/web/components/thumbnail import elekf/web/components/thumbnail
import elekf/web/components/shuffle_all
import elekf/web/components/link import elekf/web/components/link
import elekf/web/common.{type Settings} import elekf/web/common.{type Settings}
import elekf/web/router import elekf/web/router
@ -25,7 +19,6 @@ pub fn view(
library: Library, library: Library,
settings: option.Option(Settings), settings: option.Option(Settings),
item: LibraryItem(Album), item: LibraryItem(Album),
show_tracks: Bool,
) { ) {
let #(album_id, album) = item let #(album_id, album) = item
let tracks = album_utils.get_tracks(library, album) let tracks = album_utils.get_tracks(library, album)
@ -35,49 +28,23 @@ pub fn view(
} }
let assert Ok(first_track) = list.first(tracks) let assert Ok(first_track) = list.first(tracks)
list.append( [
[ link.link(
link.link( router.to_hash(router.Album(album_id)),
router.to_hash(router.Album(album_id)), "library-item album-item",
"library-item album-item", [attribute.id("album-list-" <> int.to_string(album_id))],
[attribute.id("album-list-" <> int.to_string(album_id))], [
[ thumbnail.maybe_item_thumbnail(
thumbnail.maybe_item_thumbnail( settings,
settings, { first_track.1 }.artwork_id,
{ first_track.1 }.artwork_id, album.name,
album.name, ),
), h3([attribute.class("album-item-title")], [text(album.name)]),
h3([attribute.class("album-item-title")], [text(album.name)]), p([attribute.class("album-item-artist")], [text(artist_name)]),
p([attribute.class("album-item-artist")], [text(artist_name)]), p([attribute.class("album-item-tracks-meta")], [
p([attribute.class("album-item-tracks-meta")], [ text(int.to_string(list.length(album.tracks)) <> " tracks"),
text(int.to_string(list.length(album.tracks)) <> " tracks"), ]),
]), ],
], ),
), ]
],
case show_tracks {
True -> [view_tracks(library, tracks)]
False -> []
},
)
}
pub fn view_tracks(library: Library, tracks: List(LibraryItem(Track))) {
let tracks =
list.sort(tracks, fn(a, b) { track_utils.sort_by_track_number(a.1, b.1) })
div(
[
attribute.class(
"library-item-expanded-contents album-item-expanded-tracks",
),
],
list.flatten([
[shuffle_all.view([event.on_click(ShuffleAll)])],
list.index_map(tracks, fn(track_item, index) {
track_item.view(library, tracks, index, track_item, "album-tracks-list")
})
|> list.flatten(),
]),
)
} }

View file

@ -2,7 +2,7 @@
import gleam/string import gleam/string
import gleam/list import gleam/list
import gleam/map import gleam/dict
import gleam/option import gleam/option
import gleam/dynamic import gleam/dynamic
import gleam/result import gleam/result
@ -10,11 +10,10 @@ import lustre
import lustre/effect import lustre/effect
import lustre/attribute import lustre/attribute
import lustre/element import lustre/element
import elekf/utils/misc
import elekf/library.{type Library} import elekf/library.{type Library}
import elekf/library/album.{type Album} import elekf/library/album.{type Album}
import elekf/library/album_utils import elekf/library/album_utils
import elekf/web/components/library_view.{AlbumExpandToggled} import elekf/web/components/library_view
import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_item.{type LibraryItem}
import elekf/web/components/library_views/album_item import elekf/web/components/library_views/album_item
import elekf/web/common import elekf/web/common
@ -22,7 +21,7 @@ import elekf/web/common
const component_name = "albums-view" const component_name = "albums-view"
type Model { type Model {
Model(library_view: library_view.Model(Album), expanded_album: Int) Model(library_view: library_view.Model(Album))
} }
type Msg { type Msg {
@ -37,7 +36,7 @@ pub fn register() {
update, update,
generate_view(search_filter), generate_view(search_filter),
library_view.generic_attributes() library_view.generic_attributes()
|> map.map_values(fn(_key, decoder) { |> dict.map_values(fn(_key, decoder) {
fn(data: dynamic.Dynamic) { fn(data: dynamic.Dynamic) {
data data
|> decoder() |> decoder()
@ -66,36 +65,20 @@ fn init() {
album_utils.sort_by_name, album_utils.sort_by_name,
) )
#( #(Model(library_view: lib_m), effect.map(lib_e, LibraryViewMsg))
Model(library_view: lib_m, expanded_album: library.invalid_id),
effect.map(lib_e, LibraryViewMsg),
)
} }
fn update(model: Model, msg) { fn update(model: Model, msg) {
case msg { case msg {
LibraryViewMsg(AlbumExpandToggled(album)) -> {
#(
Model(
..model,
expanded_album: misc.toggle(
model.expanded_album,
album.0,
library.invalid_id,
),
),
effect.none(),
)
}
LibraryViewMsg(lib_msg) -> { LibraryViewMsg(lib_msg) -> {
let #(lib_m, lib_e) = library_view.update(model.library_view, lib_msg) let #(lib_m, lib_e) = library_view.update(model.library_view, lib_msg)
#(Model(..model, library_view: lib_m), effect.map(lib_e, LibraryViewMsg)) #(Model(library_view: lib_m), effect.map(lib_e, LibraryViewMsg))
} }
} }
} }
fn data_getter(library: Library) { fn data_getter(library: Library) {
map.to_list(library.albums) dict.to_list(library.albums)
} }
fn shuffler(library, items: List(LibraryItem(Album))) { fn shuffler(library, items: List(LibraryItem(Album))) {
@ -121,10 +104,5 @@ fn generate_view(search_filter: library_view.SearchFilter(Album)) {
} }
fn view(model: Model, item: LibraryItem(Album)) { fn view(model: Model, item: LibraryItem(Album)) {
album_item.view( album_item.view(model.library_view.library, model.library_view.settings, item)
model.library_view.library,
model.library_view.settings,
item,
item.0 == model.expanded_album,
)
} }

View file

@ -189,12 +189,7 @@ fn view(
} }
fn album_view(model: Model, item: LibraryItem(Album)) { fn album_view(model: Model, item: LibraryItem(Album)) {
album_item.view( album_item.view(model.library_view.library, model.library_view.settings, item)
model.library_view.library,
model.library_view.settings,
item,
False,
)
} }
fn id_decode(data: dynamic.Dynamic) { fn id_decode(data: dynamic.Dynamic) {

View file

@ -2,7 +2,7 @@
import gleam/string import gleam/string
import gleam/list import gleam/list
import gleam/map import gleam/dict
import gleam/option import gleam/option
import lustre/attribute import lustre/attribute
import elekf/library.{type Library} import elekf/library.{type Library}
@ -37,7 +37,7 @@ pub fn render(
} }
fn data_getter(library: Library) { fn data_getter(library: Library) {
map.to_list(library.tracks) dict.to_list(library.tracks)
} }
fn shuffler(_library, items) { fn shuffler(_library, items) {

View file

@ -1,38 +0,0 @@
import gleam/result
import gleam/dynamic
import lustre/event
import elekf/library/artist.{type Artist}
import elekf/web/components/library_item.{type LibraryItem}
import elekf/utils/custom_event.{type CustomEvent}
const event_name = "show-artist"
pub type EventData {
EventData(artist: LibraryItem(Artist))
}
pub fn emit(artist) {
event.emit(event_name, EventData(artist))
}
pub fn on(msg: fn(LibraryItem(Artist)) -> b) {
event.on(
event_name,
fn(data) {
data
|> decoder
|> result.map(fn(e) { msg(e.artist) })
},
)
}
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)
}