diff --git a/src/elekf/library/album_utils.gleam b/src/elekf/library/album_utils.gleam index 4f0a086..0a26f1c 100644 --- a/src/elekf/library/album_utils.gleam +++ b/src/elekf/library/album_utils.gleam @@ -1,7 +1,12 @@ import gleam/list +import gleam/string +import gleam/int +import elekf/utils/order import elekf/library.{type Library} import elekf/library/album.{type Album} +const year_sorters = [year_compare, name_compare] + /// Get the individual tracks in an album. pub fn get_tracks(library: Library, album: Album) { list.map( @@ -9,3 +14,21 @@ pub fn get_tracks(library: Library, album: Album) { fn(track_id) { #(track_id, library.assert_track(library, track_id)) }, ) } + +/// Sort given albums by their lowercased names. +pub fn sort_by_name(a: Album, b: Album) { + name_compare(a, b) +} + +/// Sort given albums by their publishing year first, then lowercased names. +pub fn sort_by_year(a: Album, b: Album) { + order.compare_by_multiple(year_sorters, a, b) +} + +fn name_compare(a: Album, b: Album) { + string.compare(a.name_lower, b.name_lower) +} + +fn year_compare(a: Album, b: Album) { + int.compare(a.year, b.year) +} diff --git a/src/elekf/library/artist_utils.gleam b/src/elekf/library/artist_utils.gleam new file mode 100644 index 0000000..88710ed --- /dev/null +++ b/src/elekf/library/artist_utils.gleam @@ -0,0 +1,7 @@ +import gleam/string +import elekf/library/artist.{type Artist} + +/// Sort given artists by their lowercased names. +pub fn sort_by_name(a: Artist, b: Artist) { + string.compare(a.name_lower, b.name_lower) +} diff --git a/src/elekf/library/track_utils.gleam b/src/elekf/library/track_utils.gleam new file mode 100644 index 0000000..5ae9833 --- /dev/null +++ b/src/elekf/library/track_utils.gleam @@ -0,0 +1,24 @@ +import gleam/string +import gleam/int +import elekf/utils/order +import elekf/library/track.{type Track} + +const track_number_sorters = [order_compare, name_compare] + +/// Sort given tracks by their lowercased names. +pub fn sort_by_name(a: Track, b: Track) { + name_compare(a, b) +} + +/// Sort given tracks by their track number first, lowercased title second. +pub fn sort_by_track_number(a: Track, b: Track) { + order.compare_by_multiple(track_number_sorters, a, b) +} + +fn order_compare(a: Track, b: Track) { + int.compare(a.number, b.number) +} + +fn name_compare(a: Track, b: Track) { + string.compare(a.title_lower, b.title_lower) +} diff --git a/src/elekf/utils/order.gleam b/src/elekf/utils/order.gleam new file mode 100644 index 0000000..cc00ae3 --- /dev/null +++ b/src/elekf/utils/order.gleam @@ -0,0 +1,21 @@ +import gleam/order +import gleam/list + +/// A function that sorts two values. +pub type Sorter(a) = + fn(a, a) -> order.Order + +/// 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) + } + }, + ) +} diff --git a/src/elekf/web/components/library_view.gleam b/src/elekf/web/components/library_view.gleam index 87f7df7..3bf0d1f 100644 --- a/src/elekf/web/components/library_view.gleam +++ b/src/elekf/web/components/library_view.gleam @@ -12,6 +12,7 @@ import lustre/element.{text} import lustre/element/html.{div, h3} import lustre/attribute import lustre/event +import elekf/utils/order.{type Sorter} as order_utils import elekf/library.{type Library} import elekf/library/track.{type Track} import elekf/library/artist.{type Artist} @@ -78,6 +79,7 @@ pub type Model(a) { data: List(LibraryItem(a)), data_getter: DataGetter(a), shuffler: Shuffler(a), + sorter: Sorter(a), search: search.Model, settings: option.Option(common.Settings), ) @@ -97,11 +99,12 @@ pub fn register( data_getter: DataGetter(a), item_view: ItemView(a), shuffler: Shuffler(a), + sorter: Sorter(a), search_filter: SearchFilter(a), ) { lustre.component( name, - fn() { init(library.empty(), data_getter, shuffler) }, + fn() { init(library.empty(), data_getter, shuffler, sorter) }, update, generate_view(item_view, search_filter), generic_attributes(), @@ -134,13 +137,14 @@ pub fn render( ) } -pub fn init(library, data_getter, shuffler) { +pub fn init(library, data_getter, shuffler, sorter) { #( Model( library, data_getter(library), data_getter, shuffler, + sorter, search.init(), option.None, ), @@ -151,7 +155,13 @@ pub fn init(library, data_getter, shuffler) { pub fn update(model, msg) { case msg { LibraryUpdated(library) -> #( - Model(..model, library: library, data: model.data_getter(library)), + Model( + ..model, + library: library, + data: library + |> model.data_getter() + |> list.sort(fn(a, b) { model.sorter(a.1, b.1) }), + ), effect.none(), ) SettingsUpdated(settings) -> #( diff --git a/src/elekf/web/components/library_views/album_item.gleam b/src/elekf/web/components/library_views/album_item.gleam index 299d523..4eb06f5 100644 --- a/src/elekf/web/components/library_views/album_item.gleam +++ b/src/elekf/web/components/library_views/album_item.gleam @@ -11,6 +11,7 @@ import elekf/library.{type Library} import elekf/library/album.{type Album} import elekf/library/track.{type Track} import elekf/library/album_utils +import elekf/library/track_utils import elekf/web/components/library_view.{AlbumExpandToggled} import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_views/track_item @@ -94,6 +95,9 @@ pub fn view( } 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( diff --git a/src/elekf/web/components/library_views/albums_view.gleam b/src/elekf/web/components/library_views/albums_view.gleam index 626ae25..69c8fe5 100644 --- a/src/elekf/web/components/library_views/albums_view.gleam +++ b/src/elekf/web/components/library_views/albums_view.gleam @@ -58,7 +58,12 @@ pub fn render( fn init() { let #(lib_m, lib_e) = - library_view.init(library.empty(), data_getter, shuffler) + library_view.init( + library.empty(), + data_getter, + shuffler, + album_utils.sort_by_name, + ) #( Model(library_view: lib_m, expanded_album: library.invalid_id), diff --git a/src/elekf/web/components/library_views/artists_view.gleam b/src/elekf/web/components/library_views/artists_view.gleam index 6e47750..485ccd1 100644 --- a/src/elekf/web/components/library_views/artists_view.gleam +++ b/src/elekf/web/components/library_views/artists_view.gleam @@ -11,6 +11,7 @@ import lustre/attribute import lustre/event import elekf/library.{type Library} import elekf/library/artist.{type Artist} +import elekf/library/artist_utils import elekf/web/components/library_view.{type Model, ShowArtist} import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_views/thumbnail @@ -25,6 +26,7 @@ pub fn register() { data_getter, item_view, shuffler, + artist_utils.sort_by_name, search_filter, ) } diff --git a/src/elekf/web/components/library_views/single_artist_view.gleam b/src/elekf/web/components/library_views/single_artist_view.gleam index da2a934..22403b8 100644 --- a/src/elekf/web/components/library_views/single_artist_view.gleam +++ b/src/elekf/web/components/library_views/single_artist_view.gleam @@ -81,6 +81,7 @@ fn init() { library.empty(), fn(_) -> List(LibraryItem(Album)) { [] }, shuffler, + album_utils.sort_by_year, ) #( @@ -101,7 +102,8 @@ fn update(model: Model, msg) { expanded_album: library.invalid_id, library_view: library_view.Model( ..model.library_view, - data: data_getter(model.library_view.library, artist), + data: data_getter(model.library_view.library, artist) + |> list.sort(fn(a, b) { model.library_view.sorter(a.1, b.1) }), ), ), effect.none(), diff --git a/src/elekf/web/components/library_views/tracks_view.gleam b/src/elekf/web/components/library_views/tracks_view.gleam index f289f3a..c77275e 100644 --- a/src/elekf/web/components/library_views/tracks_view.gleam +++ b/src/elekf/web/components/library_views/tracks_view.gleam @@ -7,6 +7,7 @@ import gleam/option import lustre/attribute import elekf/library.{type Library} import elekf/library/track.{type Track} +import elekf/library/track_utils import elekf/web/components/library_view.{type Model} import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_views/track_item @@ -21,6 +22,7 @@ pub fn register() { data_getter, item_view, shuffler, + track_utils.sort_by_name, search_filter, ) }