Add documentation
This commit is contained in:
parent
4e15c49d29
commit
ea592fef6d
20 changed files with 54 additions and 1 deletions
|
@ -1,7 +1,9 @@
|
||||||
|
/// Information of the logged in user.
|
||||||
pub type User {
|
pub type User {
|
||||||
User(id: Int, token: String, session_uuid: String)
|
User(id: Int, token: String, session_uuid: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information of the user's device.
|
||||||
pub type Device {
|
pub type Device {
|
||||||
Device(name: String)
|
Device(name: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//// The auth storage stores the user's authentication credentials for future
|
||||||
|
//// use, i.e. when refreshing the page.
|
||||||
|
|
||||||
import gleam/dynamic
|
import gleam/dynamic
|
||||||
import gleam/json
|
import gleam/json
|
||||||
import plinth/javascript/storage
|
import plinth/javascript/storage
|
||||||
|
@ -6,22 +9,27 @@ import elekf/api/auth/models
|
||||||
|
|
||||||
const storage_key = "__elektrofoni_auth_storage"
|
const storage_key = "__elektrofoni_auth_storage"
|
||||||
|
|
||||||
|
/// Storage format of the authentication data in local storage.
|
||||||
pub type StorageFormat {
|
pub type StorageFormat {
|
||||||
StorageFormat(user: models.User, device: models.Device)
|
StorageFormat(user: models.User, device: models.Device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The local storage API used for storing authentication details.
|
||||||
pub type AuthStorage =
|
pub type AuthStorage =
|
||||||
varasto.TypedStorage(StorageFormat)
|
varasto.TypedStorage(StorageFormat)
|
||||||
|
|
||||||
|
/// Gets the `varasto` instance to use for reading and writing auth storage.
|
||||||
pub fn get() {
|
pub fn get() {
|
||||||
let assert Ok(local) = storage.local()
|
let assert Ok(local) = storage.local()
|
||||||
varasto.new(local, reader(), writer())
|
varasto.new(local, reader(), writer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the previously stored value, if available.
|
||||||
pub fn read(storage: AuthStorage) {
|
pub fn read(storage: AuthStorage) {
|
||||||
varasto.get(storage, storage_key)
|
varasto.get(storage, storage_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes new value to the storage.
|
||||||
pub fn write(storage: AuthStorage, data: StorageFormat) {
|
pub fn write(storage: AuthStorage, data: StorageFormat) {
|
||||||
varasto.set(storage, storage_key, data)
|
varasto.set(storage, storage_key, data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import ibroadcast/device_info.{DeviceInfo}
|
||||||
import ibroadcast/request.{RequestConfig}
|
import ibroadcast/request.{RequestConfig}
|
||||||
import elekf/utils/navigator
|
import elekf/utils/navigator
|
||||||
|
|
||||||
|
/// Gets the base request config to use for all requests, authenticated and raw.
|
||||||
pub fn base_request_config(device_name: String) {
|
pub fn base_request_config(device_name: String) {
|
||||||
RequestConfig(
|
RequestConfig(
|
||||||
app_info: elektrofoni.app_info,
|
app_info: elektrofoni.app_info,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//// The library stores the music library retrieved from iBroadcast.
|
||||||
|
|
||||||
import gleam/map.{Map}
|
import gleam/map.{Map}
|
||||||
import elekf/library/track.{Track}
|
import elekf/library/track.{Track}
|
||||||
import elekf/library/album.{Album}
|
import elekf/library/album.{Album}
|
||||||
|
@ -11,28 +13,34 @@ pub type Library {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an album from the library based on ID.
|
||||||
pub fn get_album(library: Library, id: Int) {
|
pub fn get_album(library: Library, id: Int) {
|
||||||
map.get(library.albums, id)
|
map.get(library.albums, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an artist from the library based on ID.
|
||||||
pub fn get_artist(library: Library, id: Int) {
|
pub fn get_artist(library: Library, id: Int) {
|
||||||
map.get(library.artists, id)
|
map.get(library.artists, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a track from the library based on ID.
|
||||||
pub fn get_track(library: Library, id: Int) {
|
pub fn get_track(library: Library, id: Int) {
|
||||||
map.get(library.tracks, id)
|
map.get(library.tracks, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an album from the library, asserting that it exists.
|
||||||
pub fn assert_album(library: Library, id: Int) {
|
pub fn assert_album(library: Library, id: Int) {
|
||||||
let assert Ok(album) = map.get(library.albums, id)
|
let assert Ok(album) = map.get(library.albums, id)
|
||||||
album
|
album
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an artist from the library, asserting that it exists.
|
||||||
pub fn assert_artist(library: Library, id: Int) {
|
pub fn assert_artist(library: Library, id: Int) {
|
||||||
let assert Ok(artist) = map.get(library.artists, id)
|
let assert Ok(artist) = map.get(library.artists, id)
|
||||||
artist
|
artist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a track from the library, asserting that it exists.
|
||||||
pub fn assert_track(library: Library, id: Int) {
|
pub fn assert_track(library: Library, id: Int) {
|
||||||
let assert Ok(track) = map.get(library.tracks, id)
|
let assert Ok(track) = map.get(library.tracks, id)
|
||||||
track
|
track
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
/// A track in the music library.
|
||||||
|
///
|
||||||
|
/// The `title_lower` contains the track title in lowercase, which is an
|
||||||
|
/// optimisation for searching.
|
||||||
pub type Track {
|
pub type Track {
|
||||||
Track(
|
Track(
|
||||||
number: Int,
|
number: Int,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import ibroadcast/library/library.{Album as APIAlbum}
|
import ibroadcast/library/library.{Album as APIAlbum}
|
||||||
import elekf/library/album.{Album}
|
import elekf/library/album.{Album}
|
||||||
|
|
||||||
|
/// Converts API album response to library format.
|
||||||
pub fn from(album: APIAlbum) {
|
pub fn from(album: APIAlbum) {
|
||||||
Album(
|
Album(
|
||||||
name: album.name,
|
name: album.name,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import ibroadcast/library/library.{Artist as APIArtist}
|
import ibroadcast/library/library.{Artist as APIArtist}
|
||||||
import elekf/library/artist.{Artist}
|
import elekf/library/artist.{Artist}
|
||||||
|
|
||||||
|
/// Converts API artist response to library format.
|
||||||
pub fn from(artist: APIArtist) {
|
pub fn from(artist: APIArtist) {
|
||||||
Artist(
|
Artist(
|
||||||
name: artist.name,
|
name: artist.name,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import elekf/transfer/album
|
||||||
import elekf/transfer/artist
|
import elekf/transfer/artist
|
||||||
import elekf/transfer/track
|
import elekf/transfer/track
|
||||||
|
|
||||||
|
/// Converts API library response to library format.
|
||||||
pub fn from(library: APILibrary) {
|
pub fn from(library: APILibrary) {
|
||||||
let albums = transfer_map(library.albums, album.from)
|
let albums = transfer_map(library.albums, album.from)
|
||||||
let artists = transfer_map(library.artists, artist.from)
|
let artists = transfer_map(library.artists, artist.from)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import gleam/string
|
||||||
import ibroadcast/library/library.{Track as APITrack}
|
import ibroadcast/library/library.{Track as APITrack}
|
||||||
import elekf/library/track.{Track}
|
import elekf/library/track.{Track}
|
||||||
|
|
||||||
|
/// Converts API track response to library format.
|
||||||
pub fn from(track: APITrack) {
|
pub fn from(track: APITrack) {
|
||||||
Track(
|
Track(
|
||||||
number: track.number,
|
number: track.number,
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub fn equals(a: Date, b: Date) -> Bool
|
||||||
@external(javascript, "../../date_ffi.mjs", "bigger_than")
|
@external(javascript, "../../date_ffi.mjs", "bigger_than")
|
||||||
pub fn bigger_than(a: Date, b: Date) -> Bool
|
pub fn bigger_than(a: Date, b: Date) -> Bool
|
||||||
|
|
||||||
/// Compare given dates, returning `Gt` if `a` > `b`
|
/// Compares given dates, returning `Gt` if `a` > `b`
|
||||||
pub fn compare(a: Date, b: Date) -> Order {
|
pub fn compare(a: Date, b: Date) -> Order {
|
||||||
case equals(a, b) {
|
case equals(a, b) {
|
||||||
True -> Eq
|
True -> Eq
|
||||||
|
@ -41,6 +41,7 @@ pub fn compare(a: Date, b: Date) -> Order {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes a dynamic value as an ISO 8601 formatted datetime string.
|
||||||
pub fn decode(value: Dynamic) -> Result(Date, List(DecodeError)) {
|
pub fn decode(value: Dynamic) -> Result(Date, List(DecodeError)) {
|
||||||
use str <- result.try(dynamic.string(value))
|
use str <- result.try(dynamic.string(value))
|
||||||
use date <- result.try(
|
use date <- result.try(
|
||||||
|
|
|
@ -4,9 +4,11 @@ import gleam/javascript/promise
|
||||||
import ibroadcast/request as ibroadcast_request
|
import ibroadcast/request as ibroadcast_request
|
||||||
import ibroadcast/http as ibroadcast_http
|
import ibroadcast/http as ibroadcast_http
|
||||||
|
|
||||||
|
/// Error returned by an HTTP request.
|
||||||
pub type ResponseError =
|
pub type ResponseError =
|
||||||
ibroadcast_request.ResponseError(fetch.FetchError)
|
ibroadcast_request.ResponseError(fetch.FetchError)
|
||||||
|
|
||||||
|
/// Returns an HTTP request function based on `fetch`.
|
||||||
pub fn requestor() -> ibroadcast_http.Requestor(fetch.FetchError) {
|
pub fn requestor() -> ibroadcast_http.Requestor(fetch.FetchError) {
|
||||||
fn(req: request.Request(String)) {
|
fn(req: request.Request(String)) {
|
||||||
use resp <- promise.try_await(fetch.send(req))
|
use resp <- promise.try_await(fetch.send(req))
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
/// Returns the current browser's user agent.
|
||||||
@external(javascript, "../../navigator_ffi.mjs", "userAgent")
|
@external(javascript, "../../navigator_ffi.mjs", "userAgent")
|
||||||
pub fn user_agent() -> String
|
pub fn user_agent() -> String
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//// The authed view manages the user's request config and displays the base
|
||||||
|
//// view for the app.
|
||||||
|
|
||||||
import gleam/io
|
import gleam/io
|
||||||
import gleam/int
|
import gleam/int
|
||||||
import gleam/list
|
import gleam/list
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import elekf/api/auth/models as auth_models
|
import elekf/api/auth/models as auth_models
|
||||||
|
|
||||||
|
/// Authentication data for the user.
|
||||||
pub type AuthData {
|
pub type AuthData {
|
||||||
AuthData(user: auth_models.User, device: auth_models.Device)
|
AuthData(user: auth_models.User, device: auth_models.Device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Settings of the iBroadcast API.
|
||||||
pub type Settings {
|
pub type Settings {
|
||||||
Settings(artwork_server: String, streaming_server: String)
|
Settings(artwork_server: String, streaming_server: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//// The player view renders the media player UI element and its controls.
|
||||||
|
|
||||||
import gleam/list
|
import gleam/list
|
||||||
import gleam/uri
|
import gleam/uri
|
||||||
import lustre/element.{text}
|
import lustre/element.{text}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//// The search view renders a search bar, other views must do the searching
|
||||||
|
//// based on the emitted messages.
|
||||||
|
|
||||||
import lustre/element.{text}
|
import lustre/element.{text}
|
||||||
import lustre/element/html.{button, div, input}
|
import lustre/element/html.{button, div, input}
|
||||||
import lustre/attribute
|
import lustre/attribute
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//// The login view displays the login form and starts the login operation.
|
||||||
|
|
||||||
import gleam/string
|
import gleam/string
|
||||||
import gleam/javascript/promise
|
import gleam/javascript/promise
|
||||||
import lustre/element.{text}
|
import lustre/element.{text}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//// The main view sets up everything and shows the login or authed view.
|
||||||
|
|
||||||
import gleam/io
|
import gleam/io
|
||||||
import gleam/option
|
import gleam/option
|
||||||
import lustre
|
import lustre
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/// Views that can be rendered on the main level.
|
||||||
pub type View {
|
pub type View {
|
||||||
LoginView
|
LoginView
|
||||||
AuthedView
|
AuthedView
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
import ibroadcast/app_info.{AppInfo}
|
import ibroadcast/app_info.{AppInfo}
|
||||||
|
|
||||||
|
/// Name of the application.
|
||||||
pub const app_name = "elektrofoni"
|
pub const app_name = "elektrofoni"
|
||||||
|
|
||||||
|
/// Version of the application.
|
||||||
pub const app_version = "1.0.0"
|
pub const app_version = "1.0.0"
|
||||||
|
|
||||||
|
/// ID of the application.
|
||||||
pub const app_id = 1098
|
pub const app_id = 1098
|
||||||
|
|
||||||
|
/// Constant app info to use in application.
|
||||||
pub const app_info = AppInfo(app_name, app_version)
|
pub const app_info = AppInfo(app_name, app_version)
|
||||||
|
|
||||||
|
/// The bitrate to use for streaming by default.
|
||||||
pub const bitrate = 256
|
pub const bitrate = 256
|
||||||
|
|
||||||
|
/// The expiry of the MP3 URLs generated for streaming, from the current moment
|
||||||
|
/// onwards, in milliseconds.
|
||||||
pub const track_expiry_length = 10_800_000
|
pub const track_expiry_length = 10_800_000
|
||||||
|
|
Loading…
Reference in a new issue