From d8644edd7ed78f278f97f6b941f5fb6ff2240103 Mon Sep 17 00:00:00 2001 From: Ondrej Zara Date: Tue, 26 Mar 2019 10:09:26 +0100 Subject: [PATCH] refactored ui --- app/app.css | 4 +- app/css/app.less | 4 +- app/js/fs.js | 6 +-- app/js/lib/mpd.js | 8 ++-- app/js/lib/ui.js | 94 ++++++++++++++++++++++++++++++++++++++++------- app/js/library.js | 10 ++--- app/js/queue.js | 46 +++++++---------------- 7 files changed, 109 insertions(+), 63 deletions(-) diff --git a/app/app.css b/app/app.css index a946b97..7256b06 100644 --- a/app/app.css +++ b/app/app.css @@ -1,5 +1,7 @@ body { - margin: 0; + max-width: 800px; + margin: 0 auto; + overflow: hidden; display: flex; flex-direction: column; height: 100vh; diff --git a/app/css/app.less b/app/css/app.less index ae523e4..e26d58d 100644 --- a/app/css/app.less +++ b/app/css/app.less @@ -1,5 +1,7 @@ body { - margin: 0; + max-width: 800px; + margin: 0 auto; + overflow: hidden; display: flex; flex-direction: column; height: 100vh; diff --git a/app/js/fs.js b/app/js/fs.js index 5781188..96a9edc 100644 --- a/app/js/fs.js +++ b/app/js/fs.js @@ -6,7 +6,6 @@ import * as format from "./lib/format.js"; import * as ui from "./lib/ui.js"; let node; -const SORT = "-Track"; function buildHeader(path) { filter = filter || {}; @@ -33,13 +32,14 @@ function buildHeader(path) { function buildDirectory(data, parent) { let path = data["directory"]; - let node = ui.group(path, {}, parent); + let name = path.split("/").pop(); + let node = ui.group(ui.GROUP_DIRECTORY, name, path, parent); node.addEventListener("click", e => list(path)); return node; } function buildFile(data, parent) { - return ui.song(data, parent); + return ui.song(ui.SONG_FILE, data, parent); } function buildResults(results) { diff --git a/app/js/lib/mpd.js b/app/js/lib/mpd.js index f14b521..5dd706b 100644 --- a/app/js/lib/mpd.js +++ b/app/js/lib/mpd.js @@ -77,13 +77,13 @@ export async function listQueue() { return parser.songList(lines); } -export async function enqueue(fileOrFilter, sort = null) { - if (typeof(fileOrFilter) == "string") { - return command(`addid "${escape(fileOrFilter)}"`); +export async function enqueue(urlOrFilter, sort = null) { + if (typeof(urlOrFilter) == "string") { + return command(`add "${escape(urlOrFilter)}"`); } let tokens = ["findadd"]; - tokens.push(serializeFilter(fileOrFilter)); + tokens.push(serializeFilter(urlOrFilter)); // sort && tokens.push("sort", sort); FIXME not implemented in MPD return command(tokens.join(" ")); } diff --git a/app/js/lib/ui.js b/app/js/lib/ui.js index e1b3845..c17b423 100644 --- a/app/js/lib/ui.js +++ b/app/js/lib/ui.js @@ -1,47 +1,113 @@ import * as mpd from "./mpd.js"; import * as html from "./html.js"; +import * as pubsub from "./pubsub.js"; +import * as format from "./format.js"; +import * as player from "../player.js"; -function playButton(fileOrFilter, parent) { +export const SONG_FILE = 1; +export const SONG_LIBRARY = 2; +export const SONG_QUEUE = 3; +export const GROUP_DIRECTORY = 4; +export const GROUP_LIBRARY = 5; + +const SORT = "-Track"; + +function fileName(data) { + return data["file"].split("/").pop(); +} + +function formatTitle(type, data) { + switch (type) { + case SONG_FILE: + return `🎵 ${fileName(data)}`; + break; + + case SONG_LIBRARY: + return data["Artist"] || fileName(data); + break; + + case SONG_QUEUE: + let tokens = []; + data["Artist"] && tokens.push(data["Artist"]); + data["Title"] && tokens.push(data["Title"]); + if (!tokens.length) { tokens.push(fileName(data)); } + return tokens.join(" - "); + break; + } +} + +function playButton(id, parent) { + let button = html.button({className:"play"}, "▶", parent); + button.addEventListener("click", async e => { + await mpd.command(`playid ${id}`); + player.update(); + }); +} + +function deleteButton(id, parent) { + let button = html.button({className:"delete"}, "🗙", parent); + button.addEventListener("click", async e => { + await mpd.command(`deleteid ${id}`); + pubsub.publish("queue-change"); + }); + return button; +} + +function addAndPlayButton(urlOrFilter, parent) { let button = html.button({}, "▶", parent); button.addEventListener("click", async e => { e.stopPropagation(); await mpd.command("clear"); - await mpd.enqueue(fileOrFilter, SORT); + await mpd.enqueue(urlOrFilter, SORT); await mpd.command("play"); - app.activate("queue"); + pubsub.publish("queue-change"); player.update(); }); return button; } -function addButton(fileOrFilter, parent) { +function addButton(urlOrFilter, parent) { let button = html.button({}, "+", parent); button.addEventListener("click", async e => { e.stopPropagation(); - await mpd.enqueue(fileOrFilter, SORT); + await mpd.enqueue(urlOrFilter, SORT); + pubsub.publish("queue-change"); // fixme notification? }); return button; } - -export function song(data, parent) { +export function song(type, data, parent) { let node = html.node("li", {}, "", parent); - let file = data["file"]; - playButton(file, node); - addButton(file, node); + let title = formatTitle(type, data); + html.node("h2", {}, title, node); - html.node("h3", {}, data["Title"], node); + html.node("span", {className:"duration"}, format.time(Number(data["duration"])), node); + + if (type == SONG_QUEUE) { + let id = data["Id"]; + node.dataset.songId = id; + playButton(id, node); + deleteButton(id, node); + } else { + let url = data["file"]; + addAndPlayButton(url, node); + addButton(url, node); + } return node; } -export function group(label, filter, parent) { +export function group(type, label, urlOrFilter, parent) { let node = html.node("li", {}, label, parent); - playButton(filter, node); - addButton(filter, node); + if (type == GROUP_DIRECTORY) { + node.insertBefore(html.text("📁 "), node.firstChild); + } + + addAndPlayButton(urlOrFilter, node); + addButton(urlOrFilter, node); return node; } \ No newline at end of file diff --git a/app/js/library.js b/app/js/library.js index b5d3547..269455d 100644 --- a/app/js/library.js +++ b/app/js/library.js @@ -1,12 +1,8 @@ -import * as app from "./app.js"; import * as mpd from "./lib/mpd.js"; import * as html from "./lib/html.js"; -import * as player from "./player.js"; -import * as format from "./lib/format.js"; import * as ui from "./lib/ui.js"; let node; -const SORT = "-Track"; function buildHeader(filter) { filter = filter || {}; @@ -33,14 +29,14 @@ function buildHeader(filter) { function buildAlbum(album, filter, parent) { let childFilter = Object.assign({}, filter, {"Album": album}); - let node = ui.group(album, childFilter, parent); + let node = ui.group(ui.GROUP_LIBRARY, album, childFilter, parent); node.addEventListener("click", e => listSongs(childFilter)); return node; } function buildArtist(artist, filter, parent) { let childFilter = Object.assign({}, filter, {"Artist": artist}); - let node = ui.group(artist, childFilter, parent); + let node = ui.group(ui.GROUP_LIBRARY, artist, childFilter, parent); node.addEventListener("click", e => listAlbums(childFilter)); return node; } @@ -49,7 +45,7 @@ function buildSongs(songs, filter) { let ul = node.querySelector("ul"); html.clear(ul); - songs.map(song => ui.song(song, ul)); + songs.map(song => ui.song(ui.SONG_LIBRARY, song, ul)); } function buildAlbums(albums, filter) { diff --git a/app/js/queue.js b/app/js/queue.js index 895b456..8a9d446 100644 --- a/app/js/queue.js +++ b/app/js/queue.js @@ -1,8 +1,7 @@ import * as mpd from "./lib/mpd.js"; import * as html from "./lib/html.js"; -import * as player from "./player.js"; import * as pubsub from "./lib/pubsub.js"; -import * as format from "./lib/format.js"; +import * as ui from "./lib/ui.js"; let node; let currentId; @@ -14,40 +13,11 @@ function updateCurrent() { }); } -async function playSong(id) { - await mpd.command(`playid ${id}`); - player.update(); -} - -async function deleteSong(id) { - await mpd.command(`deleteid ${id}`); - activate(); -} - -function buildSong(song) { - let id = Number(song["Id"]); - - let node = html.node("li"); - node.dataset.songId = id; - - html.button({className:"play"}, "▶", node).addEventListener("click", e => playSong(id)); - - let info = html.node("div", {className:"info"}, "", node); - - html.node("h2", {className:"title"}, song["Title"], info); - html.node("span", {className:"artist-album"}, format.artistAlbum(song["Artist"], song["Album"]), info); - html.node("span", {className:"duration"}, format.time(Number(song["duration"])), info); - - html.button({className:"delete"}, "🗙", node).addEventListener("click", e => deleteSong(id)); - - return node; -} - function buildSongs(songs) { let ul = node.querySelector("ul"); html.clear(ul); - songs.map(buildSong).forEach(li => ul.appendChild(li)); + songs.map(song => ui.song(ui.SONG_QUEUE, song, ul)); updateCurrent(); } @@ -57,12 +27,22 @@ function onSongChange(message, publisher, data) { updateCurrent(); } -export async function activate() { +function onQueueChange(message, publisher, data) { + syncQueue(); +} + +async function syncQueue() { let songs = await mpd.listQueue(); buildSongs(songs); +// FIXME updateCount(songs.length) +} + +export async function activate() { + syncQueue(); } export function init(n) { node = n; pubsub.subscribe("song-change", onSongChange); + pubsub.subscribe("queue-change", onQueueChange); }