Initial real routing work
This commit is contained in:
parent
22f8574973
commit
7b3e06beb3
4 changed files with 155 additions and 57 deletions
|
@ -6,12 +6,12 @@ import gleam/list
|
||||||
import gleam/option
|
import gleam/option
|
||||||
import gleam/float
|
import gleam/float
|
||||||
import gleam/int
|
import gleam/int
|
||||||
|
import gleam/result
|
||||||
import gleam/javascript/promise
|
import gleam/javascript/promise
|
||||||
import lustre/element.{text}
|
import lustre/element.{text}
|
||||||
import lustre/element/html.{div, nav, p}
|
import lustre/element/html.{div, nav, p}
|
||||||
import lustre/attribute
|
import lustre/attribute
|
||||||
import lustre/effect
|
import lustre/effect
|
||||||
import lustre/event
|
|
||||||
import birl
|
import birl
|
||||||
import ibroadcast/library/library as library_api
|
import ibroadcast/library/library as library_api
|
||||||
import ibroadcast/authed_request.{type RequestConfig, RequestConfig}
|
import ibroadcast/authed_request.{type RequestConfig, RequestConfig}
|
||||||
|
@ -23,6 +23,7 @@ import elekf/library.{type Library}
|
||||||
import elekf/library/artist.{type Artist}
|
import elekf/library/artist.{type Artist}
|
||||||
import elekf/library/track.{type Track}
|
import elekf/library/track.{type Track}
|
||||||
import elekf/transfer/library as library_transfer
|
import elekf/transfer/library as library_transfer
|
||||||
|
import elekf/web/router
|
||||||
import elekf/web/components/player
|
import elekf/web/components/player
|
||||||
import elekf/web/components/library_item.{type LibraryItem}
|
import elekf/web/components/library_item.{type LibraryItem}
|
||||||
import elekf/web/components/library_view
|
import elekf/web/components/library_view
|
||||||
|
@ -31,7 +32,7 @@ import elekf/web/components/library_views/artists_view
|
||||||
import elekf/web/components/library_views/albums_view
|
import elekf/web/components/library_views/albums_view
|
||||||
import elekf/web/components/library_views/single_artist_view
|
import elekf/web/components/library_views/single_artist_view
|
||||||
import elekf/web/components/button_group
|
import elekf/web/components/button_group
|
||||||
import elekf/web/components/button
|
import elekf/web/components/link
|
||||||
import elekf/web/events/start_play
|
import elekf/web/events/start_play
|
||||||
import elekf/web/events/show_artist
|
import elekf/web/events/show_artist
|
||||||
import elekf/web/utils
|
import elekf/web/utils
|
||||||
|
@ -69,7 +70,7 @@ pub type Model {
|
||||||
settings: option.Option(common.Settings),
|
settings: option.Option(common.Settings),
|
||||||
request_config: RequestConfig,
|
request_config: RequestConfig,
|
||||||
play_status: PlayStatus,
|
play_status: PlayStatus,
|
||||||
view_stack: List(library_view.View),
|
view: library_view.View,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +79,7 @@ pub type Msg {
|
||||||
LibraryResult(Result(library_api.ResponseData, http.ResponseError))
|
LibraryResult(Result(library_api.ResponseData, http.ResponseError))
|
||||||
PlayerMsg(player.Msg)
|
PlayerMsg(player.Msg)
|
||||||
StartPlay(PlayQueue, Int)
|
StartPlay(PlayQueue, Int)
|
||||||
ShowArtist(LibraryItem(Artist))
|
Router(router.Msg)
|
||||||
ChangeView(library_view.View)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(auth_data: common.AuthData) {
|
pub fn init(auth_data: common.AuthData) {
|
||||||
|
@ -90,10 +90,14 @@ pub fn init(auth_data: common.AuthData) {
|
||||||
settings: option.None,
|
settings: option.None,
|
||||||
request_config: form_request_config(auth_data),
|
request_config: form_request_config(auth_data),
|
||||||
play_status: NoTracks,
|
play_status: NoTracks,
|
||||||
view_stack: [library_view.Tracks],
|
view: library_view.Tracks,
|
||||||
)
|
)
|
||||||
|
|
||||||
#(model, load_library(model))
|
let router_effect =
|
||||||
|
effect.from(fn(dispatch) { router.init(dispatch) })
|
||||||
|
|> effect.map(Router)
|
||||||
|
|
||||||
|
#(model, effect.batch([load_library(model), router_effect]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(model: Model, msg) {
|
pub fn update(model: Model, msg) {
|
||||||
|
@ -144,15 +148,6 @@ pub fn update(model: Model, msg) {
|
||||||
let #(status, effect) = handle_start_play(model, tracks, position)
|
let #(status, effect) = handle_start_play(model, tracks, position)
|
||||||
#(Model(..model, play_status: status), effect)
|
#(Model(..model, play_status: status), effect)
|
||||||
}
|
}
|
||||||
ShowArtist(artist) -> #(
|
|
||||||
Model(
|
|
||||||
..model,
|
|
||||||
view_stack: list.append(model.view_stack, [
|
|
||||||
library_view.SingleArtist(artist),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
effect.none(),
|
|
||||||
)
|
|
||||||
PlayerMsg(player.NextTrack) | PlayerMsg(player.PrevTrack) -> {
|
PlayerMsg(player.NextTrack) | PlayerMsg(player.PrevTrack) -> {
|
||||||
if_player(model, fn(info) {
|
if_player(model, fn(info) {
|
||||||
let next_index = case msg {
|
let next_index = case msg {
|
||||||
|
@ -187,8 +182,9 @@ pub fn update(model: Model, msg) {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ChangeView(view) -> {
|
Router(router.RouteChanged(route)) -> {
|
||||||
#(Model(..model, view_stack: [view]), effect.none())
|
let view = result.unwrap(route_to_view(route), library_view.Tracks)
|
||||||
|
#(Model(..model, view: view), effect.none())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,50 +205,42 @@ pub fn view(model: Model) {
|
||||||
div([attribute.id("authed-view-library")], [
|
div([attribute.id("authed-view-library")], [
|
||||||
nav([attribute.id("library-top-nav")], [
|
nav([attribute.id("library-top-nav")], [
|
||||||
button_group.view("", [], [
|
button_group.view("", [], [
|
||||||
button.view("", [event.on_click(ChangeView(library_view.Tracks))], [
|
link.view(router.to_hash(router.TrackList), "", [], [
|
||||||
icon("music-note-beamed", Alt("Tracks")),
|
icon("music-note-beamed", Alt("Tracks")),
|
||||||
]),
|
]),
|
||||||
button.view("", [event.on_click(ChangeView(library_view.Artists))], [
|
link.view(router.to_hash(router.ArtistList), "", [], [
|
||||||
icon("file-person", Alt("Artists")),
|
icon("file-person", Alt("Artists")),
|
||||||
]),
|
]),
|
||||||
button.view("", [event.on_click(ChangeView(library_view.Albums))], [
|
link.view(router.to_hash(router.AlbumList), "", [], [
|
||||||
icon("disc", Alt("Albums")),
|
icon("disc", Alt("Albums")),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
..list.map(model.view_stack, fn(view) {
|
case model.view {
|
||||||
case view {
|
library_view.Tracks ->
|
||||||
library_view.Tracks ->
|
tracks_view.render(model.library, model.settings, [
|
||||||
tracks_view.render(model.library, model.settings, [
|
attribute.id("tracks-view"),
|
||||||
attribute.id("tracks-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(model.library, model.settings, [
|
||||||
artists_view.render(model.library, model.settings, [
|
attribute.id("artists-view"),
|
||||||
attribute.id("artists-view"),
|
attribute.class("glass-bg"),
|
||||||
attribute.class("glass-bg"),
|
])
|
||||||
show_artist.on(ShowArtist),
|
library_view.Albums ->
|
||||||
])
|
albums_view.render(model.library, model.settings, [
|
||||||
library_view.Albums ->
|
attribute.id("albums-view"),
|
||||||
albums_view.render(model.library, model.settings, [
|
attribute.class("glass-bg"),
|
||||||
attribute.id("albums-view"),
|
start_play.on(StartPlay),
|
||||||
attribute.class("glass-bg"),
|
])
|
||||||
start_play.on(StartPlay),
|
library_view.SingleArtist(artist_info) ->
|
||||||
])
|
single_artist_view.render(model.library, artist_info, model.settings, [
|
||||||
library_view.SingleArtist(artist_info) ->
|
attribute.id("single-artist-view"),
|
||||||
single_artist_view.render(
|
attribute.class("glass-bg"),
|
||||||
model.library,
|
start_play.on(StartPlay),
|
||||||
artist_info,
|
])
|
||||||
model.settings,
|
},
|
||||||
[
|
|
||||||
attribute.id("single-artist-view"),
|
|
||||||
attribute.class("glass-bg"),
|
|
||||||
start_play.on(StartPlay),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]),
|
]),
|
||||||
div(
|
div(
|
||||||
[
|
[
|
||||||
|
@ -388,3 +376,11 @@ fn maybe_send_history_status(play_info: PlayInfo, request_config: RequestConfig)
|
||||||
HistorySent -> play_info
|
HistorySent -> play_info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn route_to_view(route: router.Route) {
|
||||||
|
case route {
|
||||||
|
router.TrackList -> Ok(library_view.Tracks)
|
||||||
|
router.ArtistList -> Ok(library_view.Artists)
|
||||||
|
router.AlbumList -> Ok(library_view.Albums)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
19
src/elekf/web/components/link.gleam
Normal file
19
src/elekf/web/components/link.gleam
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import lustre/attribute
|
||||||
|
import lustre/element
|
||||||
|
import lustre/element/html.{a}
|
||||||
|
|
||||||
|
pub fn view(
|
||||||
|
href: String,
|
||||||
|
class: String,
|
||||||
|
extra_attributes: List(attribute.Attribute(a)),
|
||||||
|
content: List(element.Element(a)),
|
||||||
|
) {
|
||||||
|
a(
|
||||||
|
[
|
||||||
|
attribute.href(href),
|
||||||
|
attribute.class("glass-button " <> class),
|
||||||
|
..extra_attributes
|
||||||
|
],
|
||||||
|
content,
|
||||||
|
)
|
||||||
|
}
|
76
src/elekf/web/router.gleam
Normal file
76
src/elekf/web/router.gleam
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import gleam/int
|
||||||
|
import gleam/string
|
||||||
|
import gleam/result
|
||||||
|
import plinth/browser/window
|
||||||
|
|
||||||
|
const artists = "artists"
|
||||||
|
|
||||||
|
const albums = "albums"
|
||||||
|
|
||||||
|
const settings = "settings"
|
||||||
|
|
||||||
|
pub type Msg {
|
||||||
|
RouteChanged(route: Route)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Route {
|
||||||
|
TrackList
|
||||||
|
ArtistList
|
||||||
|
AlbumList
|
||||||
|
Artist(id: Int)
|
||||||
|
Album(id: Int)
|
||||||
|
Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(dispatch: fn(Msg) -> Nil) {
|
||||||
|
window.add_event_listener("hashchange", fn(_e) {
|
||||||
|
let _ = run(dispatch)
|
||||||
|
Nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(dispatch: fn(Msg) -> Nil) {
|
||||||
|
use hash <- result.try(window.get_hash())
|
||||||
|
use route <- result.try(parse(hash))
|
||||||
|
Ok(dispatch(RouteChanged(route)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_hash(route: Route) {
|
||||||
|
"#" <> to_string(route)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(route: String) {
|
||||||
|
let parts =
|
||||||
|
route
|
||||||
|
|> string.drop_left(1)
|
||||||
|
|> string.split("/")
|
||||||
|
|
||||||
|
case parts {
|
||||||
|
[""] -> Ok(TrackList)
|
||||||
|
[a] if a == artists -> Ok(ArtistList)
|
||||||
|
[a] if a == albums -> Ok(AlbumList)
|
||||||
|
[a, id] -> {
|
||||||
|
case a, int.parse(id) {
|
||||||
|
a, Ok(id) if a == artists -> Ok(Artist(id))
|
||||||
|
a, Ok(id) if a == albums -> Ok(Album(id))
|
||||||
|
_, _ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[s] if s == settings -> Ok(Settings)
|
||||||
|
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_string(route: Route) {
|
||||||
|
let parts = case route {
|
||||||
|
TrackList -> []
|
||||||
|
ArtistList -> [artists]
|
||||||
|
AlbumList -> [albums]
|
||||||
|
Artist(id) -> [artists, int.to_string(id)]
|
||||||
|
Album(id) -> [albums, int.to_string(id)]
|
||||||
|
Settings -> [settings]
|
||||||
|
}
|
||||||
|
|
||||||
|
"/" <> string.join(parts, "/")
|
||||||
|
}
|
13
style.css
13
style.css
|
@ -70,6 +70,10 @@
|
||||||
|
|
||||||
.glass-button {
|
.glass-button {
|
||||||
border-radius: var(--button-border-radius);
|
border-radius: var(--button-border-radius);
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-small,
|
.button-small,
|
||||||
|
@ -89,6 +93,10 @@
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-group > * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.button-group .glass-button {
|
.button-group .glass-button {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
|
@ -296,10 +304,9 @@ single-artist-view {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#library-top-nav button {
|
#library-top-nav .glass-button {
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-bar {
|
#search-bar {
|
||||||
|
|
Loading…
Reference in a new issue