Search works

This commit is contained in:
Mikko Ahlroth 2023-10-15 20:23:50 +03:00
parent bf31c2fd87
commit a8ed5aa8ff
4 changed files with 48 additions and 41 deletions

View file

@ -2,16 +2,12 @@
//// view for the app.
import gleam/io
import gleam/int
import gleam/list
import gleam/option
import gleam/map
import gleam/string
import gleam/javascript/promise
import lustre/element.{text}
import lustre/element/html.{div, h3, p}
import lustre/element/html.{div, p}
import lustre/attribute
import lustre/event
import lustre/effect
import elekf/web/common.{PlayQueue}
import ibroadcast/library/library as library_api
@ -22,7 +18,6 @@ import elekf/library.{Library}
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/events/start_play
import elekf/web/utils
@ -48,7 +43,6 @@ pub type Model {
library: Library,
settings: option.Option(common.Settings),
request_config: RequestConfig,
search: search.Model,
play_status: PlayStatus,
)
}
@ -57,7 +51,6 @@ pub type Msg {
UpdateAuthData(common.AuthData)
LibraryResult(Result(library_api.ResponseData, http.ResponseError))
PlayerMsg(player.Msg)
Search(search.Msg)
StartPlay(PlayQueue, Int)
}
@ -68,7 +61,6 @@ pub fn init(auth_data: common.AuthData) {
library: library.empty(),
settings: option.None,
request_config: form_request_config(auth_data),
search: search.init(),
play_status: NoTracks,
)
@ -165,17 +157,10 @@ pub fn update(model: Model, msg) {
},
)
}
Search(msg) -> {
let search_model = search.update(model.search, msg)
#(Model(..model, search: search_model), effect.none())
}
}
}
pub fn view(model: Model) {
let search_text = string.lowercase(model.search.search_text)
div(
[attribute.id("authed-view-wrapper")],
[
@ -200,13 +185,6 @@ pub fn view(model: Model) {
div(
[attribute.id("authed-view-player")],
[
div(
[attribute.id("search-positioner")],
[
search.view(model.search)
|> element.map(Search),
],
),
case model.play_status {
HasTracks(PlayInfo(player: player, ..)) ->
div(

View file

@ -16,6 +16,7 @@ import elekf/library/album.{Album}
import elekf/library/artist.{Artist}
import elekf/library/track.{Track}
import elekf/web/events/start_play
import elekf/web/components/search
/// An item in the library with its ID.
pub type LibraryItem(a) =
@ -29,6 +30,13 @@ pub type DataGetter(a) =
pub type ItemView(a) =
fn(Library, List(LibraryItem(a)), Int, LibraryItem(a)) -> element.Element(Msg)
/// 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.
///
/// The search string is lowercased.
pub type SearchFilter(a) =
fn(a, String) -> Bool
/// Shuffler takes all of the items, finds all of the tracks in them, and
/// shuffles them for playback.
pub type Shuffler(a) =
@ -53,6 +61,7 @@ pub type Msg {
LibraryUpdated(Library)
ShuffleAll
StartPlay(List(LibraryItem(Track)), Int)
Search(search.Msg)
}
pub type Model(a) {
@ -61,6 +70,7 @@ pub type Model(a) {
data: List(LibraryItem(a)),
data_getter: DataGetter(a),
shuffler: Shuffler(a),
search: search.Model,
)
}
@ -78,12 +88,13 @@ pub fn register(
data_getter: DataGetter(a),
item_view: ItemView(a),
shuffler: Shuffler(a),
search_filter: SearchFilter(a),
) {
lustre.component(
name,
fn() { init(library.empty(), data_getter, shuffler) },
update,
generate_view(item_view),
generate_view(item_view, search_filter),
map.from_list([#("library", library_decode)]),
)
}
@ -102,7 +113,10 @@ pub fn render(
}
fn init(library, data_getter, shuffler) {
#(Model(library, data_getter(library), data_getter, shuffler), effect.none())
#(
Model(library, data_getter(library), data_getter, shuffler, search.init()),
effect.none(),
)
}
fn update(model, msg) {
@ -113,15 +127,28 @@ fn update(model, msg) {
)
ShuffleAll -> #(model, shuffle_all(model))
StartPlay(tracks, position) -> #(model, start_play.emit(tracks, position))
Search(search_msg) -> {
let search_model = search.update(model.search, search_msg)
#(Model(..model, search: search_model), effect.none())
}
}
}
fn generate_view(item_view: ItemView(a)) {
fn generate_view(item_view: ItemView(a), search_filter: SearchFilter(a)) {
fn(model: Model(a)) {
let items = case model.search.search_text {
"" -> model.data
txt ->
model.data
|> list.filter(fn(item) { search_filter(item.1, txt) })
}
div(
[attribute.class("library-list")],
list.append(
[
search.view(model.search)
|> element.map(Search),
div(
[
attribute.class("library-item library-list-shuffle-all"),
@ -131,7 +158,7 @@ fn generate_view(item_view: ItemView(a)) {
),
],
list.index_map(
model.data,
items,
fn(i, item) { item_view(model.library, model.data, i, item) },
),
),

View file

@ -1,5 +1,6 @@
//// A library view to all of the tracks in the library.
import gleam/string
import gleam/int
import gleam/list
import gleam/map
@ -15,7 +16,13 @@ const component_name = "tracks-view"
/// Register the tracks view as a custom element.
pub fn register() {
library_view.register(component_name, data_getter, item_view, shuffler)
library_view.register(
component_name,
data_getter,
item_view,
shuffler,
search_filter,
)
}
/// Render the tracks view.
@ -31,6 +38,10 @@ fn shuffler(items) {
list.shuffle(items)
}
fn search_filter(item: Track, search_text: String) {
string.contains(item.title_lower, search_text)
}
fn item_view(
library: Library,
items: List(LibraryItem(Track)),

View file

@ -30,17 +30,12 @@ main {
overflow-y: auto;
}
#player {
}
#search-positioner {
position: relative;
}
#search-bar {
position: absolute;
transform: translateY(-200%);
width: 100%;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: calc(100vw - 20px);
display: flex;
flex-direction: row;
@ -49,10 +44,6 @@ main {
gap: 10px;
}
#search-bar button {
justify-self: flex-end;
}
#search-bar-input-wrapper {
flex: 1 1;
}