Initial work for play queue
This commit is contained in:
parent
5b8f4fdd2d
commit
33f8cd92f2
8 changed files with 173 additions and 387 deletions
|
@ -8,14 +8,15 @@ pub type Sorter(a) =
|
|||
/// Compare two values by using multiple sorters. The comparison will stop after
|
||||
/// the first sorter that returns something other than `order.Eq`.
|
||||
pub fn compare_by_multiple(sorters: List(Sorter(a)), a: a, b: a) {
|
||||
list.fold_until(
|
||||
sorters,
|
||||
order.Eq,
|
||||
fn(prev, sorter) {
|
||||
case prev {
|
||||
order.Eq -> list.Continue(sorter(a, b))
|
||||
other -> list.Stop(other)
|
||||
}
|
||||
},
|
||||
)
|
||||
list.fold_until(sorters, order.Eq, fn(prev, sorter) {
|
||||
case prev {
|
||||
order.Eq -> list.Continue(sorter(a, b))
|
||||
other -> list.Stop(other)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// A sorter that does not sort, keeping the order of the elements.
|
||||
pub fn noop(_a, _b) {
|
||||
order.Eq
|
||||
}
|
||||
|
|
|
@ -27,17 +27,17 @@ import elekf/web/common
|
|||
import elekf/web/storage/history/storage as history_store
|
||||
|
||||
/// Function to get the data of the view from the library.
|
||||
pub type DataGetter(a) =
|
||||
fn(Library) -> List(LibraryItem(a))
|
||||
pub type DataGetter(a, filter) =
|
||||
fn(Library, filter) -> List(LibraryItem(a))
|
||||
|
||||
/// A view that renders a single item from the library.
|
||||
///
|
||||
/// It should return a list of elements, which can be used for expanding items:
|
||||
/// the first element for the item itself and subsequent elements for the
|
||||
/// expanded contents.
|
||||
pub type ItemView(a) =
|
||||
fn(Model(a), List(LibraryItem(a)), Int, LibraryItem(a)) ->
|
||||
List(element.Element(Msg))
|
||||
pub type ItemView(a, filter) =
|
||||
fn(Model(a, filter), List(LibraryItem(a)), Int, LibraryItem(a)) ->
|
||||
List(element.Element(Msg(filter)))
|
||||
|
||||
/// A filter that gets the item and the current search string and must return
|
||||
/// `True` if the item should be shown and `False` if not.
|
||||
|
@ -66,24 +66,25 @@ pub type View {
|
|||
Tracks
|
||||
}
|
||||
|
||||
pub type Msg {
|
||||
pub type Msg(filter) {
|
||||
LibraryUpdated(Library)
|
||||
SettingsUpdated(option.Option(common.Settings))
|
||||
ShuffleAll
|
||||
StartPlay(List(LibraryItem(Track)), Int)
|
||||
Search(search.Msg)
|
||||
FilterUpdated
|
||||
FilterUpdated(filter)
|
||||
ListScrolled(Float)
|
||||
ScrollRequested(Float)
|
||||
}
|
||||
|
||||
pub type Model(a) {
|
||||
pub type Model(a, filter) {
|
||||
Model(
|
||||
id: String,
|
||||
filter: filter,
|
||||
library: Library,
|
||||
library_loading: Bool,
|
||||
data: List(LibraryItem(a)),
|
||||
data_getter: DataGetter(a),
|
||||
data_getter: DataGetter(a, filter),
|
||||
shuffler: Shuffler(a),
|
||||
sorter: Sorter(a),
|
||||
search: search.Model,
|
||||
|
@ -104,18 +105,30 @@ pub type Model(a) {
|
|||
/// and shuffle them for play.
|
||||
pub fn register(
|
||||
name: String,
|
||||
data_getter: DataGetter(a),
|
||||
item_view: ItemView(a),
|
||||
default_filter: filter,
|
||||
data_getter: DataGetter(a, filter),
|
||||
item_view: ItemView(a, filter),
|
||||
shuffler: Shuffler(a),
|
||||
sorter: Sorter(a),
|
||||
search_filter: SearchFilter(a),
|
||||
extra_attrs: dict.Dict(String, dynamic.Decoder(Msg(filter))),
|
||||
) {
|
||||
lustre.component(
|
||||
name,
|
||||
fn() { init(name, library.empty(), True, data_getter, shuffler, sorter) },
|
||||
fn() {
|
||||
init(
|
||||
name,
|
||||
default_filter,
|
||||
library.empty(),
|
||||
True,
|
||||
data_getter,
|
||||
shuffler,
|
||||
sorter,
|
||||
)
|
||||
},
|
||||
update,
|
||||
generate_view(item_view, search_filter),
|
||||
generic_attributes(),
|
||||
dict.merge(generic_attributes(), extra_attrs),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -146,7 +159,7 @@ pub fn render(
|
|||
@external(javascript, "../../../library_view_ffi.mjs", "requestScroll")
|
||||
pub fn request_scroll(pos: Float) -> Nil
|
||||
|
||||
pub fn init(id, library, library_loading, data_getter, shuffler, sorter) {
|
||||
pub fn init(id, filter, library, library_loading, data_getter, shuffler, sorter) {
|
||||
let scrollend_effect =
|
||||
effect.from(fn(dispatch) {
|
||||
lustre_utils.after_next_render(fn() {
|
||||
|
@ -169,9 +182,10 @@ pub fn init(id, library, library_loading, data_getter, shuffler, sorter) {
|
|||
#(
|
||||
Model(
|
||||
id,
|
||||
filter,
|
||||
library,
|
||||
library_loading,
|
||||
data_getter(library),
|
||||
data_getter(library, filter),
|
||||
data_getter,
|
||||
shuffler,
|
||||
sorter,
|
||||
|
@ -202,7 +216,10 @@ pub fn update(model, msg) {
|
|||
#(Model(..model, search: search_model), effect.none())
|
||||
}
|
||||
|
||||
FilterUpdated -> #(update_data(model, model.library), effect.none())
|
||||
FilterUpdated(filter) -> {
|
||||
let new_model = Model(..model, filter: filter)
|
||||
#(update_data(new_model, new_model.library), effect.none())
|
||||
}
|
||||
|
||||
ListScrolled(pos) -> {
|
||||
let view_history = get_view_history(model.history_api)
|
||||
|
@ -224,8 +241,8 @@ pub fn update(model, msg) {
|
|||
}
|
||||
|
||||
pub fn library_view(
|
||||
model: Model(a),
|
||||
item_view: ItemView(a),
|
||||
model: Model(a, filter),
|
||||
item_view: ItemView(a, filter),
|
||||
search_filter: SearchFilter(a),
|
||||
) {
|
||||
let items = case model.search.search_text {
|
||||
|
@ -251,11 +268,11 @@ pub fn library_view(
|
|||
)
|
||||
}
|
||||
|
||||
fn generate_view(item_view: ItemView(a), search_filter: SearchFilter(a)) {
|
||||
fn generate_view(item_view: ItemView(a, filter), search_filter: SearchFilter(a)) {
|
||||
library_view(_, item_view, search_filter)
|
||||
}
|
||||
|
||||
fn shuffle_all(model: Model(a)) {
|
||||
fn shuffle_all(model: Model(a, filter)) {
|
||||
let tracks = model.shuffler(model.library, model.data)
|
||||
start_play.emit(tracks, 0)
|
||||
}
|
||||
|
@ -270,11 +287,11 @@ fn settings_decode(data: dynamic.Dynamic) {
|
|||
Ok(SettingsUpdated(settings))
|
||||
}
|
||||
|
||||
fn update_data(model: Model(a), library: Library) {
|
||||
fn update_data(model: Model(a, filter), library: Library) {
|
||||
Model(
|
||||
..model,
|
||||
data: library
|
||||
|> model.data_getter()
|
||||
|> model.data_getter(model.filter)
|
||||
|> list.sort(fn(a, b) { model.sorter(a.1, b.1) }),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,45 +4,28 @@ import gleam/string
|
|||
import gleam/list
|
||||
import gleam/dict
|
||||
import gleam/option
|
||||
import gleam/dynamic
|
||||
import gleam/result
|
||||
import lustre
|
||||
import lustre/effect
|
||||
import lustre/attribute
|
||||
import lustre/element
|
||||
import elekf/library.{type Library}
|
||||
import elekf/library/album.{type Album}
|
||||
import elekf/library/album_utils
|
||||
import elekf/web/components/library_view
|
||||
import elekf/web/components/library_view.{type Model}
|
||||
import elekf/web/components/library_item.{type LibraryItem}
|
||||
import elekf/web/components/library_views/album_item
|
||||
import elekf/web/common
|
||||
|
||||
const component_name = "albums-view"
|
||||
|
||||
type Model {
|
||||
Model(library_view: library_view.Model(Album))
|
||||
}
|
||||
|
||||
type Msg {
|
||||
LibraryViewMsg(library_view.Msg)
|
||||
}
|
||||
|
||||
/// Register the albums view as a custom element.
|
||||
pub fn register() {
|
||||
lustre.component(
|
||||
library_view.register(
|
||||
component_name,
|
||||
init,
|
||||
update,
|
||||
generate_view(search_filter),
|
||||
library_view.generic_attributes()
|
||||
|> dict.map_values(fn(_key, decoder) {
|
||||
fn(data: dynamic.Dynamic) {
|
||||
data
|
||||
|> decoder()
|
||||
|> result.map(LibraryViewMsg)
|
||||
}
|
||||
}),
|
||||
Nil,
|
||||
data_getter,
|
||||
item_view,
|
||||
shuffler,
|
||||
album_utils.sort_by_name,
|
||||
search_filter,
|
||||
dict.new(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -55,30 +38,7 @@ pub fn render(
|
|||
library_view.render(component_name, library, settings, extra_attrs)
|
||||
}
|
||||
|
||||
fn init() {
|
||||
let #(lib_m, lib_e) =
|
||||
library_view.init(
|
||||
component_name,
|
||||
library.empty(),
|
||||
True,
|
||||
data_getter,
|
||||
shuffler,
|
||||
album_utils.sort_by_name,
|
||||
)
|
||||
|
||||
#(Model(library_view: lib_m), effect.map(lib_e, LibraryViewMsg))
|
||||
}
|
||||
|
||||
fn update(model: Model, msg) {
|
||||
case msg {
|
||||
LibraryViewMsg(lib_msg) -> {
|
||||
let #(lib_m, lib_e) = library_view.update(model.library_view, lib_msg)
|
||||
#(Model(library_view: lib_m), effect.map(lib_e, LibraryViewMsg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn data_getter(library: Library) {
|
||||
fn data_getter(library: Library, _filter: Nil) {
|
||||
dict.to_list(library.albums)
|
||||
}
|
||||
|
||||
|
@ -93,17 +53,11 @@ fn search_filter(item: Album, search_text: String) {
|
|||
string.contains(item.name_lower, search_text)
|
||||
}
|
||||
|
||||
fn generate_view(search_filter: library_view.SearchFilter(Album)) {
|
||||
fn(model: Model) {
|
||||
library_view.library_view(
|
||||
model.library_view,
|
||||
fn(_library_model, _items, _index, item) { view(model, item) },
|
||||
search_filter,
|
||||
)
|
||||
|> element.map(LibraryViewMsg)
|
||||
}
|
||||
}
|
||||
|
||||
fn view(model: Model, item: LibraryItem(Album)) {
|
||||
album_item.view(model.library_view.library, model.library_view.settings, item)
|
||||
fn item_view(
|
||||
model: Model(Album, Nil),
|
||||
_items: List(LibraryItem(Album)),
|
||||
_index: Int,
|
||||
item: LibraryItem(Album),
|
||||
) {
|
||||
album_item.view(model.library, model.settings, item)
|
||||
}
|
||||
|
|
|
@ -24,11 +24,13 @@ const component_name = "artists-view"
|
|||
pub fn register() {
|
||||
library_view.register(
|
||||
component_name,
|
||||
Nil,
|
||||
data_getter,
|
||||
item_view,
|
||||
shuffler,
|
||||
artist_utils.sort_by_name,
|
||||
search_filter,
|
||||
dict.new(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,7 @@ pub fn render(
|
|||
library_view.render(component_name, library, settings, extra_attrs)
|
||||
}
|
||||
|
||||
fn data_getter(library: Library) {
|
||||
fn data_getter(library: Library, _filter: Nil) {
|
||||
library.artists
|
||||
|> dict.fold([], fn(acc, key, val) {
|
||||
case val.tracks {
|
||||
|
@ -63,7 +65,7 @@ fn search_filter(item: Artist, search_text: String) {
|
|||
}
|
||||
|
||||
fn item_view(
|
||||
model: Model(Artist),
|
||||
model: Model(Artist, Nil),
|
||||
_items: List(LibraryItem(Artist)),
|
||||
_index: Int,
|
||||
item: LibraryItem(Artist),
|
||||
|
|
60
src/elekf/web/components/library_views/play_queue_view.gleam
Normal file
60
src/elekf/web/components/library_views/play_queue_view.gleam
Normal file
|
@ -0,0 +1,60 @@
|
|||
//// A library view to the current play queue.
|
||||
|
||||
import gleam/string
|
||||
import gleam/list
|
||||
import gleam/dict
|
||||
import gleam/option
|
||||
import lustre/attribute
|
||||
import elekf/library.{type Library}
|
||||
import elekf/library/track.{type Track}
|
||||
import elekf/utils/order
|
||||
import elekf/web/components/library_view.{type Model} as library_view
|
||||
import elekf/web/components/library_item.{type LibraryItem}
|
||||
import elekf/web/components/library_views/track_item
|
||||
import elekf/web/common
|
||||
|
||||
const component_name = "play-queue-view"
|
||||
|
||||
/// Register the play queue view as a custom element.
|
||||
pub fn register() {
|
||||
library_view.register(
|
||||
component_name,
|
||||
Nil,
|
||||
data_getter,
|
||||
item_view,
|
||||
shuffler,
|
||||
order.noop,
|
||||
search_filter,
|
||||
dict.new(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Render the play queue view.
|
||||
pub fn render(
|
||||
library: Library,
|
||||
settings: option.Option(common.Settings),
|
||||
extra_attrs: List(attribute.Attribute(msg)),
|
||||
) {
|
||||
library_view.render(component_name, library, settings, extra_attrs)
|
||||
}
|
||||
|
||||
fn data_getter(library: Library, _filter: Nil) {
|
||||
dict.to_list(library.tracks)
|
||||
}
|
||||
|
||||
fn shuffler(_library, items) {
|
||||
list.shuffle(items)
|
||||
}
|
||||
|
||||
fn search_filter(item: Track, search_text: String) {
|
||||
string.contains(item.title_lower, search_text)
|
||||
}
|
||||
|
||||
fn item_view(
|
||||
model: Model(Track, Nil),
|
||||
items: List(LibraryItem(Track)),
|
||||
index: Int,
|
||||
item: LibraryItem(Track),
|
||||
) {
|
||||
track_item.view(model.library, items, index, item, "track-list")
|
||||
}
|
|
@ -5,15 +5,8 @@ import gleam/list
|
|||
import gleam/dict
|
||||
import gleam/option
|
||||
import gleam/dynamic
|
||||
import gleam/result
|
||||
import gleam/int
|
||||
import lustre
|
||||
import lustre/element/html.{div, header}
|
||||
import lustre/element.{text}
|
||||
import lustre/attribute
|
||||
import lustre/effect
|
||||
import elekf/library.{type Library}
|
||||
import elekf/library/album.{type Album}
|
||||
import elekf/library/track.{type Track}
|
||||
import elekf/library/track_utils
|
||||
import elekf/web/components/library_view
|
||||
|
@ -23,37 +16,17 @@ import elekf/web/common
|
|||
|
||||
const component_name = "single-album-view"
|
||||
|
||||
type Model {
|
||||
Model(
|
||||
library_view: library_view.Model(Track),
|
||||
album_id: Int,
|
||||
album: option.Option(Album),
|
||||
)
|
||||
}
|
||||
|
||||
type Msg {
|
||||
LibraryViewMsg(library_view.Msg)
|
||||
AlbumUpdated(Int)
|
||||
}
|
||||
|
||||
/// Register the single album view as a custom element.
|
||||
pub fn register() {
|
||||
lustre.component(
|
||||
library_view.register(
|
||||
component_name,
|
||||
init,
|
||||
update,
|
||||
generate_view(search_filter),
|
||||
dict.merge(
|
||||
library_view.generic_attributes()
|
||||
|> dict.map_values(fn(_key, decoder) {
|
||||
fn(data: dynamic.Dynamic) {
|
||||
data
|
||||
|> decoder()
|
||||
|> result.map(LibraryViewMsg)
|
||||
}
|
||||
}),
|
||||
dict.from_list([#("album-id", id_decode)]),
|
||||
),
|
||||
library.invalid_id,
|
||||
data_getter,
|
||||
item_view,
|
||||
shuffler,
|
||||
track_utils.sort_by_track_number,
|
||||
search_filter,
|
||||
dict.from_list([#("album-id", id_decode)]),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -70,67 +43,6 @@ pub fn render(
|
|||
])
|
||||
}
|
||||
|
||||
fn init() {
|
||||
let #(lib_m, lib_e) =
|
||||
library_view.init(
|
||||
component_name,
|
||||
library.empty(),
|
||||
True,
|
||||
fn(_) -> List(LibraryItem(Track)) { [] },
|
||||
shuffler,
|
||||
track_utils.sort_by_track_number,
|
||||
)
|
||||
|
||||
#(
|
||||
Model(album_id: library.invalid_id, album: option.None, library_view: lib_m),
|
||||
effect.map(lib_e, LibraryViewMsg),
|
||||
)
|
||||
}
|
||||
|
||||
fn update(model: Model, msg) {
|
||||
case msg {
|
||||
AlbumUpdated(id) -> {
|
||||
let new_getter = data_getter(_, id)
|
||||
|
||||
let album =
|
||||
load_album(model.library_view.library, id)
|
||||
|> option.from_result()
|
||||
|
||||
#(
|
||||
Model(
|
||||
album_id: id,
|
||||
album: album,
|
||||
library_view: library_view.Model(
|
||||
..model.library_view,
|
||||
data_getter: new_getter,
|
||||
),
|
||||
),
|
||||
effect.map(
|
||||
effect.from(fn(dispatch) { dispatch(library_view.FilterUpdated) }),
|
||||
LibraryViewMsg,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
LibraryViewMsg(lib_msg) -> {
|
||||
let #(lib_m, lib_e) = library_view.update(model.library_view, lib_msg)
|
||||
|
||||
// Update album when library is updated
|
||||
let album = case lib_msg {
|
||||
library_view.LibraryUpdated(new_lib) ->
|
||||
load_album(new_lib, model.album_id)
|
||||
|> option.from_result()
|
||||
_ -> model.album
|
||||
}
|
||||
|
||||
#(
|
||||
Model(..model, album: album, library_view: lib_m),
|
||||
effect.map(lib_e, LibraryViewMsg),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn data_getter(library: Library, album_id: Int) {
|
||||
library.tracks
|
||||
|> dict.fold([], fn(acc, key, val) {
|
||||
|
@ -150,52 +62,16 @@ fn search_filter(item: Track, search_text: String) {
|
|||
string.contains(item.title_lower, search_text)
|
||||
}
|
||||
|
||||
fn generate_view(search_filter: library_view.SearchFilter(Track)) {
|
||||
fn(model: Model) {
|
||||
case model.library_view.library_loading, model.album {
|
||||
True, _ ->
|
||||
div([attribute.class("library-view-loading")], [
|
||||
text("Loading library…"),
|
||||
])
|
||||
False, option.None ->
|
||||
div([attribute.class("library-view-not-specified")], [
|
||||
text("Album not found with ID: " <> int.to_string(model.album_id)),
|
||||
])
|
||||
False, option.Some(album) ->
|
||||
view(model, #(model.album_id, album), search_filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(
|
||||
model: Model,
|
||||
album: LibraryItem(Album),
|
||||
search_filter: library_view.SearchFilter(Track),
|
||||
fn item_view(
|
||||
model: library_view.Model(Track, Int),
|
||||
items: List(LibraryItem(Track)),
|
||||
index: Int,
|
||||
item: LibraryItem(Track),
|
||||
) {
|
||||
div([], [
|
||||
header([attribute.class("library-header")], [text({ album.1 }.name)]),
|
||||
library_view.library_view(
|
||||
model.library_view,
|
||||
fn(library, items, index, item) {
|
||||
track_item.view(
|
||||
library.library,
|
||||
items,
|
||||
index,
|
||||
item,
|
||||
"album-tracks-list",
|
||||
)
|
||||
},
|
||||
search_filter,
|
||||
)
|
||||
|> element.map(LibraryViewMsg),
|
||||
])
|
||||
track_item.view(model.library, items, index, item, "album-tracks-list")
|
||||
}
|
||||
|
||||
fn id_decode(data: dynamic.Dynamic) {
|
||||
let album_id: Int = dynamic.unsafe_coerce(data)
|
||||
Ok(AlbumUpdated(album_id))
|
||||
}
|
||||
|
||||
fn load_album(library: Library, id: Int) {
|
||||
library.get_album(library, id)
|
||||
Ok(library_view.FilterUpdated(album_id))
|
||||
}
|
||||
|
|
|
@ -5,55 +5,28 @@ import gleam/list
|
|||
import gleam/dict
|
||||
import gleam/option
|
||||
import gleam/dynamic
|
||||
import gleam/result
|
||||
import gleam/int
|
||||
import lustre
|
||||
import lustre/element/html.{div, header}
|
||||
import lustre/element.{text}
|
||||
import lustre/attribute
|
||||
import lustre/effect
|
||||
import elekf/library.{type Library}
|
||||
import elekf/library/album.{type Album}
|
||||
import elekf/library/album_utils
|
||||
import elekf/library/artist.{type Artist}
|
||||
import elekf/library/track.{type Track}
|
||||
import elekf/library/track_utils
|
||||
import elekf/web/components/library_view
|
||||
import elekf/web/components/library_item.{type LibraryItem}
|
||||
import elekf/web/components/library_views/album_item
|
||||
import elekf/web/components/library_views/track_item
|
||||
import elekf/web/common
|
||||
|
||||
const component_name = "single-artist-view"
|
||||
|
||||
type Model {
|
||||
Model(
|
||||
library_view: library_view.Model(Album),
|
||||
artist_id: Int,
|
||||
artist: option.Option(Artist),
|
||||
)
|
||||
}
|
||||
|
||||
type Msg {
|
||||
LibraryViewMsg(library_view.Msg)
|
||||
ArtistUpdated(Int)
|
||||
}
|
||||
|
||||
/// Register the single artist view as a custom element.
|
||||
pub fn register() {
|
||||
lustre.component(
|
||||
library_view.register(
|
||||
component_name,
|
||||
init,
|
||||
update,
|
||||
generate_view(search_filter),
|
||||
dict.merge(
|
||||
library_view.generic_attributes()
|
||||
|> dict.map_values(fn(_key, decoder) {
|
||||
fn(data: dynamic.Dynamic) {
|
||||
data
|
||||
|> decoder()
|
||||
|> result.map(LibraryViewMsg)
|
||||
}
|
||||
}),
|
||||
dict.from_list([#("artist-id", id_decode)]),
|
||||
),
|
||||
library.invalid_id,
|
||||
data_getter,
|
||||
item_view,
|
||||
shuffler,
|
||||
track_utils.sort_by_name,
|
||||
search_filter,
|
||||
dict.from_list([#("artist-id", id_decode)]),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -70,134 +43,35 @@ pub fn render(
|
|||
])
|
||||
}
|
||||
|
||||
fn init() {
|
||||
let #(lib_m, lib_e) =
|
||||
library_view.init(
|
||||
component_name,
|
||||
library.empty(),
|
||||
True,
|
||||
fn(_) -> List(LibraryItem(Album)) { [] },
|
||||
shuffler,
|
||||
album_utils.sort_by_year,
|
||||
)
|
||||
|
||||
#(
|
||||
Model(
|
||||
artist_id: library.invalid_id,
|
||||
artist: option.None,
|
||||
library_view: lib_m,
|
||||
),
|
||||
effect.map(lib_e, LibraryViewMsg),
|
||||
)
|
||||
}
|
||||
|
||||
fn update(model: Model, msg) {
|
||||
case msg {
|
||||
ArtistUpdated(id) -> {
|
||||
let new_getter = data_getter(_, id)
|
||||
|
||||
let artist =
|
||||
load_artist(model.library_view.library, id)
|
||||
|> option.from_result()
|
||||
|
||||
#(
|
||||
Model(
|
||||
artist_id: id,
|
||||
artist: artist,
|
||||
library_view: library_view.Model(
|
||||
..model.library_view,
|
||||
data_getter: new_getter,
|
||||
),
|
||||
),
|
||||
effect.map(
|
||||
effect.from(fn(dispatch) { dispatch(library_view.FilterUpdated) }),
|
||||
LibraryViewMsg,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
LibraryViewMsg(lib_msg) -> {
|
||||
let #(lib_m, lib_e) = library_view.update(model.library_view, lib_msg)
|
||||
|
||||
// Update artist when library is updated
|
||||
let artist = case lib_msg {
|
||||
library_view.LibraryUpdated(new_lib) ->
|
||||
load_artist(new_lib, model.artist_id)
|
||||
|> option.from_result()
|
||||
_ -> model.artist
|
||||
}
|
||||
|
||||
#(
|
||||
Model(..model, artist: artist, library_view: lib_m),
|
||||
effect.map(lib_e, LibraryViewMsg),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn data_getter(library: Library, artist_id: Int) {
|
||||
library.albums
|
||||
library.tracks
|
||||
|> dict.fold([], fn(acc, key, val) {
|
||||
case val.artist_id == artist_id {
|
||||
case val.artist_id == artist_id && val.album_id == 0 {
|
||||
True -> [#(key, val), ..acc]
|
||||
False -> acc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn shuffler(library, items: List(LibraryItem(Album))) {
|
||||
fn shuffler(_library, items: List(LibraryItem(Track))) {
|
||||
items
|
||||
|> list.shuffle()
|
||||
|> list.map(fn(album) { album_utils.get_tracks(library, album.1) })
|
||||
|> list.flatten()
|
||||
}
|
||||
|
||||
fn search_filter(item: Album, search_text: String) {
|
||||
string.contains(item.name_lower, search_text)
|
||||
fn search_filter(item: Track, search_text: String) {
|
||||
string.contains(item.title_lower, search_text)
|
||||
}
|
||||
|
||||
fn generate_view(search_filter: library_view.SearchFilter(Album)) {
|
||||
fn(model: Model) {
|
||||
case model.library_view.library_loading, model.artist {
|
||||
True, _ ->
|
||||
div([attribute.class("library-view-loading")], [
|
||||
text("Loading library…"),
|
||||
])
|
||||
False, option.None ->
|
||||
div([attribute.class("library-view-not-specified")], [
|
||||
text("Artist not found with ID: " <> int.to_string(model.artist_id)),
|
||||
])
|
||||
False, option.Some(artist) ->
|
||||
view(model, #(model.artist_id, artist), search_filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(
|
||||
model: Model,
|
||||
artist: LibraryItem(Artist),
|
||||
search_filter: library_view.SearchFilter(Album),
|
||||
fn item_view(
|
||||
model: library_view.Model(Track, Int),
|
||||
items: List(LibraryItem(Track)),
|
||||
index: Int,
|
||||
item: LibraryItem(Track),
|
||||
) {
|
||||
div([], [
|
||||
header([attribute.class("library-header")], [text({ artist.1 }.name)]),
|
||||
library_view.library_view(
|
||||
model.library_view,
|
||||
fn(_library_model, _items, _index, item) { album_view(model, item) },
|
||||
search_filter,
|
||||
)
|
||||
|> element.map(LibraryViewMsg),
|
||||
])
|
||||
}
|
||||
|
||||
fn album_view(model: Model, item: LibraryItem(Album)) {
|
||||
album_item.view(model.library_view.library, model.library_view.settings, item)
|
||||
track_item.view(model.library, items, index, item, "artist-tracks-list")
|
||||
}
|
||||
|
||||
fn id_decode(data: dynamic.Dynamic) {
|
||||
let artist_id: Int = dynamic.unsafe_coerce(data)
|
||||
Ok(ArtistUpdated(artist_id))
|
||||
}
|
||||
|
||||
fn load_artist(library: Library, id: Int) {
|
||||
library.get_artist(library, id)
|
||||
Ok(library_view.FilterUpdated(artist_id))
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ const component_name = "tracks-view"
|
|||
pub fn register() {
|
||||
library_view.register(
|
||||
component_name,
|
||||
Nil,
|
||||
data_getter,
|
||||
item_view,
|
||||
shuffler,
|
||||
track_utils.sort_by_name,
|
||||
search_filter,
|
||||
dict.new(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -36,7 +38,7 @@ pub fn render(
|
|||
library_view.render(component_name, library, settings, extra_attrs)
|
||||
}
|
||||
|
||||
fn data_getter(library: Library) {
|
||||
fn data_getter(library: Library, _filter: Nil) {
|
||||
dict.to_list(library.tracks)
|
||||
}
|
||||
|
||||
|
@ -49,7 +51,7 @@ fn search_filter(item: Track, search_text: String) {
|
|||
}
|
||||
|
||||
fn item_view(
|
||||
model: Model(Track),
|
||||
model: Model(Track, Nil),
|
||||
items: List(LibraryItem(Track)),
|
||||
index: Int,
|
||||
item: LibraryItem(Track),
|
||||
|
|
Loading…
Reference in a new issue