Fix events and scrolling from history
This commit is contained in:
parent
8570d4e39e
commit
f4450206b6
6 changed files with 63 additions and 79 deletions
|
@ -12,7 +12,7 @@ packages = [
|
||||||
{ name = "gleam_otp", version = "0.11.2", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "517FFB679E44AD71D059F3EF6A17BA6EFC8CB94FA174D52E22FB6768CF684D78" },
|
{ name = "gleam_otp", version = "0.11.2", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "517FFB679E44AD71D059F3EF6A17BA6EFC8CB94FA174D52E22FB6768CF684D78" },
|
||||||
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
|
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
|
||||||
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
|
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
|
||||||
{ name = "lustre", version = "4.3.5", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "BED65BD6A23439FB155A5BE166ED3C6BC20DED844FA9BB21840860951BC8E153" },
|
{ name = "lustre", version = "4.3.6", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "A122A11C761E2366EEA4649CBBE5C162BCA8EF5D78C0B1C7AACBA6E824AE9F5D" },
|
||||||
{ name = "plinth", version = "0.4.12", build_tools = ["gleam"], requirements = ["conversation", "gleam_javascript", "gleam_json", "gleam_stdlib"], otp_app = "plinth", source = "hex", outer_checksum = "3727B517C05BD4D49A4AF34BA7E63E03CA77B4E8AD2A4DCE60266B28717C1F9A" },
|
{ name = "plinth", version = "0.4.12", build_tools = ["gleam"], requirements = ["conversation", "gleam_javascript", "gleam_json", "gleam_stdlib"], otp_app = "plinth", source = "hex", outer_checksum = "3727B517C05BD4D49A4AF34BA7E63E03CA77B4E8AD2A4DCE60266B28717C1F9A" },
|
||||||
{ name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" },
|
{ name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" },
|
||||||
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
|
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
|
||||||
|
|
|
@ -289,19 +289,19 @@ pub fn view(model: Model) {
|
||||||
case model.view {
|
case model.view {
|
||||||
library_view.Tracks ->
|
library_view.Tracks ->
|
||||||
tracks_view.render(library_if_loaded(model), model.settings, [
|
tracks_view.render(library_if_loaded(model), model.settings, [
|
||||||
attribute.id("tracks-view"),
|
attribute.id("library-view"),
|
||||||
attribute.class("glass-bg"),
|
attribute.class("glass-bg"),
|
||||||
start_play.on(StartPlay),
|
start_play.on(StartPlay),
|
||||||
])
|
])
|
||||||
library_view.Artists ->
|
library_view.Artists ->
|
||||||
artists_view.render(library_if_loaded(model), model.settings, [
|
artists_view.render(library_if_loaded(model), model.settings, [
|
||||||
attribute.id("artists-view"),
|
attribute.id("library-view"),
|
||||||
attribute.class("glass-bg"),
|
attribute.class("glass-bg"),
|
||||||
start_play.on(StartPlay),
|
start_play.on(StartPlay),
|
||||||
])
|
])
|
||||||
library_view.Albums ->
|
library_view.Albums ->
|
||||||
albums_view.render(library_if_loaded(model), model.settings, [
|
albums_view.render(library_if_loaded(model), model.settings, [
|
||||||
attribute.id("albums-view"),
|
attribute.id("library-view"),
|
||||||
attribute.class("glass-bg"),
|
attribute.class("glass-bg"),
|
||||||
start_play.on(StartPlay),
|
start_play.on(StartPlay),
|
||||||
])
|
])
|
||||||
|
@ -311,7 +311,7 @@ pub fn view(model: Model) {
|
||||||
id,
|
id,
|
||||||
model.settings,
|
model.settings,
|
||||||
[
|
[
|
||||||
attribute.id("single-artist-view"),
|
attribute.id("library-view"),
|
||||||
attribute.class("glass-bg"),
|
attribute.class("glass-bg"),
|
||||||
start_play.on(StartPlay),
|
start_play.on(StartPlay),
|
||||||
],
|
],
|
||||||
|
@ -322,7 +322,7 @@ pub fn view(model: Model) {
|
||||||
id,
|
id,
|
||||||
model.settings,
|
model.settings,
|
||||||
[
|
[
|
||||||
attribute.id("single-album-view"),
|
attribute.id("library-view"),
|
||||||
attribute.class("glass-bg"),
|
attribute.class("glass-bg"),
|
||||||
start_play.on(StartPlay),
|
start_play.on(StartPlay),
|
||||||
],
|
],
|
||||||
|
|
|
@ -108,7 +108,6 @@ pub type Model(a, filter) {
|
||||||
sorter: Sorter(a),
|
sorter: Sorter(a),
|
||||||
search: search.Model,
|
search: search.Model,
|
||||||
settings: option.Option(common.Settings),
|
settings: option.Option(common.Settings),
|
||||||
history: history_store.StorageFormat,
|
|
||||||
history_api: history_store.HistoryStorage,
|
history_api: history_store.HistoryStorage,
|
||||||
styles_path: String,
|
styles_path: String,
|
||||||
)
|
)
|
||||||
|
@ -175,24 +174,9 @@ pub fn render(
|
||||||
pub fn request_scroll(pos: Float) -> Nil
|
pub fn request_scroll(pos: Float) -> Nil
|
||||||
|
|
||||||
pub fn init(id, filter, data_getter, shuffler, sorter, styles_path) {
|
pub fn init(id, filter, data_getter, shuffler, sorter, styles_path) {
|
||||||
let scrollend_effect =
|
|
||||||
effect.from(fn(dispatch) {
|
|
||||||
lustre_utils.after_next_render(fn() {
|
|
||||||
add_scrollend_listener(fn(pos) { dispatch(ListScrolled(pos)) })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
let history_api = history_store.get_api()
|
let history_api = history_store.get_api()
|
||||||
let history = get_view_history(history_api)
|
|
||||||
|
|
||||||
let scroll_to = dict.get(history.scrolls, id)
|
let scroll_to_effect = get_history_scroll(history_api, id)
|
||||||
let scroll_to_effect = case scroll_to {
|
|
||||||
Ok(pos) if pos >. 0.0 ->
|
|
||||||
effect.from(fn(dispatch) {
|
|
||||||
lustre_utils.after_next_render(fn() { dispatch(ScrollRequested(pos)) })
|
|
||||||
})
|
|
||||||
_ -> effect.none()
|
|
||||||
}
|
|
||||||
|
|
||||||
#(
|
#(
|
||||||
Model(
|
Model(
|
||||||
|
@ -205,20 +189,26 @@ pub fn init(id, filter, data_getter, shuffler, sorter, styles_path) {
|
||||||
sorter:,
|
sorter:,
|
||||||
search: search.init(),
|
search: search.init(),
|
||||||
settings: option.None,
|
settings: option.None,
|
||||||
history:,
|
|
||||||
history_api:,
|
history_api:,
|
||||||
styles_path:,
|
styles_path:,
|
||||||
),
|
),
|
||||||
effect.batch([scrollend_effect, scroll_to_effect]),
|
scroll_to_effect,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(model: Model(a, filter), msg) {
|
pub fn update(model: Model(a, filter), msg) {
|
||||||
case msg {
|
case msg {
|
||||||
LibraryUpdated(library) -> #(
|
LibraryUpdated(library) -> {
|
||||||
update_data(Model(..model, library_status: new_library(library))),
|
let scroll_effect = case model.library_status {
|
||||||
effect.none(),
|
NoLibrary -> get_history_scroll(model.history_api, model.id)
|
||||||
|
_ -> effect.none()
|
||||||
|
}
|
||||||
|
|
||||||
|
#(
|
||||||
|
update_data(Model(..model, library_status: HaveLibrary(library))),
|
||||||
|
scroll_effect,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
SettingsUpdated(settings) -> #(
|
SettingsUpdated(settings) -> #(
|
||||||
Model(..model, settings: settings),
|
Model(..model, settings: settings),
|
||||||
effect.none(),
|
effect.none(),
|
||||||
|
@ -280,7 +270,11 @@ pub fn library_view(
|
||||||
element.fragment([
|
element.fragment([
|
||||||
styles,
|
styles,
|
||||||
div(
|
div(
|
||||||
[attribute.id("library-list"), scroll_to.on(ScrollRequested)],
|
[
|
||||||
|
attribute.id("library-list"),
|
||||||
|
event.on("scrollend", handle_scrollend),
|
||||||
|
scroll_to.on(ScrollRequested),
|
||||||
|
],
|
||||||
list.append(
|
list.append(
|
||||||
[
|
[
|
||||||
search.view(model.search)
|
search.view(model.search)
|
||||||
|
@ -407,16 +401,29 @@ fn update_data(model: Model(a, filter)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_history_scroll(history_api: history_store.HistoryStorage, id: String) {
|
||||||
|
let history = get_view_history(history_api).scrolls
|
||||||
|
let scroll_to = dict.get(history, id)
|
||||||
|
case scroll_to {
|
||||||
|
Ok(pos) if pos >. 0.0 ->
|
||||||
|
effect.from(fn(dispatch) {
|
||||||
|
lustre_utils.after_next_render(fn() { dispatch(ScrollRequested(pos)) })
|
||||||
|
})
|
||||||
|
_ -> effect.none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_view_history(history_api) {
|
fn get_view_history(history_api) {
|
||||||
result.unwrap(history_store.read(history_api), history_store.new())
|
result.unwrap(history_store.read(history_api), history_store.new())
|
||||||
}
|
}
|
||||||
|
|
||||||
@external(javascript, "../../../library_view_ffi.mjs", "addScrollendListener")
|
fn handle_scrollend(e: dynamic.Dynamic) {
|
||||||
fn add_scrollend_listener(callback: fn(Float) -> Nil) -> Nil
|
use position <- result.try(dynamic.field(
|
||||||
|
"target",
|
||||||
|
dynamic.field("scrollTop", dynamic.float),
|
||||||
|
)(e))
|
||||||
|
Ok(ListScrolled(position))
|
||||||
|
}
|
||||||
|
|
||||||
@external(javascript, "../../../library_view_ffi.mjs", "scrollTo")
|
@external(javascript, "../../../library_view_ffi.mjs", "scrollTo")
|
||||||
fn scroll_to(pos: Float) -> Nil
|
fn scroll_to(pos: Float) -> Nil
|
||||||
|
|
||||||
fn new_library(library: Library) -> LibraryStatus {
|
|
||||||
HaveLibrary(library)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import elekf/utils/custom_event.{type CustomEvent}
|
import elekf/utils/custom_event
|
||||||
import forbidden_stdlib/dynamic as forbidden_dynamic
|
|
||||||
import gleam/dynamic
|
import gleam/dynamic
|
||||||
import gleam/json
|
import gleam/json
|
||||||
import gleam/result
|
import gleam/result
|
||||||
|
@ -12,7 +11,7 @@ pub type EventData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit(pos: Float) {
|
pub fn emit(pos: Float) {
|
||||||
event.emit(event_name, json.float(pos))
|
event.emit(event_name, json.object([#("pos", json.float(pos))]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on(msg: fn(Float) -> b) {
|
pub fn on(msg: fn(Float) -> b) {
|
||||||
|
@ -24,18 +23,13 @@ pub fn on(msg: fn(Float) -> b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn js_custom_event(pos: Float) {
|
pub fn js_custom_event(pos: Float) {
|
||||||
custom_event.new(event_name, EventData(pos))
|
custom_event.new(event_name, json.object([#("pos", json.float(pos))]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decoder(data: dynamic.Dynamic) {
|
fn decoder(data: dynamic.Dynamic) {
|
||||||
let e: CustomEvent = forbidden_dynamic.unsafe_coerce(data)
|
use detail <- result.try(dynamic.field(
|
||||||
use detail <- result.try(
|
"detail",
|
||||||
custom_event.get_detail(e)
|
dynamic.field("pos", dynamic.float),
|
||||||
|> result.replace_error([
|
)(data))
|
||||||
dynamic.DecodeError("event detail", "no detail", []),
|
Ok(detail)
|
||||||
]),
|
|
||||||
)
|
|
||||||
use event_data <- result.try(dynamic.float(detail))
|
|
||||||
|
|
||||||
Ok(event_data)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import elekf/utils/custom_event.{type CustomEvent}
|
|
||||||
import elekf/web/codec/play_queue as play_queue_codec
|
import elekf/web/codec/play_queue as play_queue_codec
|
||||||
import elekf/web/play_queue.{type PlayQueue}
|
import elekf/web/play_queue.{type PlayQueue}
|
||||||
import forbidden_stdlib/dynamic as forbidden_dynamic
|
|
||||||
import gleam/dynamic
|
import gleam/dynamic
|
||||||
import gleam/json
|
import gleam/json
|
||||||
import gleam/result
|
import gleam/result
|
||||||
|
@ -32,18 +30,14 @@ pub fn on(msg: fn(PlayQueue, Int) -> b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decoder(data: dynamic.Dynamic) {
|
fn decoder(data: dynamic.Dynamic) {
|
||||||
let e: CustomEvent = forbidden_dynamic.unsafe_coerce(data)
|
use detail <- result.try(dynamic.field(
|
||||||
use detail <- result.try(
|
"detail",
|
||||||
custom_event.get_detail(e)
|
dynamic.decode2(
|
||||||
|> result.replace_error([
|
|
||||||
dynamic.DecodeError("event detail", "no detail", []),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
use event_data <- result.try(dynamic.decode2(
|
|
||||||
EventData,
|
EventData,
|
||||||
dynamic.field("queue", play_queue_codec.decode),
|
dynamic.field("queue", play_queue_codec.decode),
|
||||||
dynamic.field("position", dynamic.int),
|
dynamic.field("position", dynamic.int),
|
||||||
)(detail))
|
),
|
||||||
|
)(data))
|
||||||
|
|
||||||
Ok(event_data)
|
Ok(detail)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,6 @@ export function requestScroll(pos) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addScrollendListener(callback) {
|
|
||||||
const el = getEl();
|
|
||||||
if (el) {
|
|
||||||
el.addEventListener(
|
|
||||||
"scrollend",
|
|
||||||
() => {
|
|
||||||
callback(el.scrollTop);
|
|
||||||
},
|
|
||||||
{ passive: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function scrollTo(pos) {
|
export function scrollTo(pos) {
|
||||||
const el = getEl();
|
const el = getEl();
|
||||||
if (el) {
|
if (el) {
|
||||||
|
@ -32,5 +19,7 @@ export function scrollTo(pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEl() {
|
function getEl() {
|
||||||
return document.getElementById("library-list");
|
return document
|
||||||
|
.getElementById("library-view")
|
||||||
|
.shadowRoot?.getElementById("library-list");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue