refactored ui
This commit is contained in:
parent
0e43a33c4a
commit
d8644edd7e
7 changed files with 109 additions and 63 deletions
|
@ -1,5 +1,7 @@
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
|
@ -6,7 +6,6 @@ import * as format from "./lib/format.js";
|
||||||
import * as ui from "./lib/ui.js";
|
import * as ui from "./lib/ui.js";
|
||||||
|
|
||||||
let node;
|
let node;
|
||||||
const SORT = "-Track";
|
|
||||||
|
|
||||||
function buildHeader(path) {
|
function buildHeader(path) {
|
||||||
filter = filter || {};
|
filter = filter || {};
|
||||||
|
@ -33,13 +32,14 @@ function buildHeader(path) {
|
||||||
|
|
||||||
function buildDirectory(data, parent) {
|
function buildDirectory(data, parent) {
|
||||||
let path = data["directory"];
|
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));
|
node.addEventListener("click", e => list(path));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFile(data, parent) {
|
function buildFile(data, parent) {
|
||||||
return ui.song(data, parent);
|
return ui.song(ui.SONG_FILE, data, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildResults(results) {
|
function buildResults(results) {
|
||||||
|
|
|
@ -77,13 +77,13 @@ export async function listQueue() {
|
||||||
return parser.songList(lines);
|
return parser.songList(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enqueue(fileOrFilter, sort = null) {
|
export async function enqueue(urlOrFilter, sort = null) {
|
||||||
if (typeof(fileOrFilter) == "string") {
|
if (typeof(urlOrFilter) == "string") {
|
||||||
return command(`addid "${escape(fileOrFilter)}"`);
|
return command(`add "${escape(urlOrFilter)}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tokens = ["findadd"];
|
let tokens = ["findadd"];
|
||||||
tokens.push(serializeFilter(fileOrFilter));
|
tokens.push(serializeFilter(urlOrFilter));
|
||||||
// sort && tokens.push("sort", sort); FIXME not implemented in MPD
|
// sort && tokens.push("sort", sort); FIXME not implemented in MPD
|
||||||
return command(tokens.join(" "));
|
return command(tokens.join(" "));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,113 @@
|
||||||
import * as mpd from "./mpd.js";
|
import * as mpd from "./mpd.js";
|
||||||
import * as html from "./html.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);
|
let button = html.button({}, "▶", parent);
|
||||||
button.addEventListener("click", async e => {
|
button.addEventListener("click", async e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await mpd.command("clear");
|
await mpd.command("clear");
|
||||||
await mpd.enqueue(fileOrFilter, SORT);
|
await mpd.enqueue(urlOrFilter, SORT);
|
||||||
await mpd.command("play");
|
await mpd.command("play");
|
||||||
app.activate("queue");
|
pubsub.publish("queue-change");
|
||||||
player.update();
|
player.update();
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addButton(fileOrFilter, parent) {
|
function addButton(urlOrFilter, parent) {
|
||||||
let button = html.button({}, "+", parent);
|
let button = html.button({}, "+", parent);
|
||||||
button.addEventListener("click", async e => {
|
button.addEventListener("click", async e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await mpd.enqueue(fileOrFilter, SORT);
|
await mpd.enqueue(urlOrFilter, SORT);
|
||||||
|
pubsub.publish("queue-change");
|
||||||
// fixme notification?
|
// fixme notification?
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function song(type, data, parent) {
|
||||||
export function song(data, parent) {
|
|
||||||
let node = html.node("li", {}, "", parent);
|
let node = html.node("li", {}, "", parent);
|
||||||
|
|
||||||
let file = data["file"];
|
let title = formatTitle(type, data);
|
||||||
playButton(file, node);
|
html.node("h2", {}, title, node);
|
||||||
addButton(file, 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;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function group(label, filter, parent) {
|
export function group(type, label, urlOrFilter, parent) {
|
||||||
let node = html.node("li", {}, label, parent);
|
let node = html.node("li", {}, label, parent);
|
||||||
|
|
||||||
playButton(filter, node);
|
if (type == GROUP_DIRECTORY) {
|
||||||
addButton(filter, node);
|
node.insertBefore(html.text("📁 "), node.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAndPlayButton(urlOrFilter, node);
|
||||||
|
addButton(urlOrFilter, node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
|
@ -1,12 +1,8 @@
|
||||||
import * as app from "./app.js";
|
|
||||||
import * as mpd from "./lib/mpd.js";
|
import * as mpd from "./lib/mpd.js";
|
||||||
import * as html from "./lib/html.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";
|
import * as ui from "./lib/ui.js";
|
||||||
|
|
||||||
let node;
|
let node;
|
||||||
const SORT = "-Track";
|
|
||||||
|
|
||||||
function buildHeader(filter) {
|
function buildHeader(filter) {
|
||||||
filter = filter || {};
|
filter = filter || {};
|
||||||
|
@ -33,14 +29,14 @@ function buildHeader(filter) {
|
||||||
|
|
||||||
function buildAlbum(album, filter, parent) {
|
function buildAlbum(album, filter, parent) {
|
||||||
let childFilter = Object.assign({}, filter, {"Album": album});
|
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));
|
node.addEventListener("click", e => listSongs(childFilter));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildArtist(artist, filter, parent) {
|
function buildArtist(artist, filter, parent) {
|
||||||
let childFilter = Object.assign({}, filter, {"Artist": artist});
|
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));
|
node.addEventListener("click", e => listAlbums(childFilter));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +45,7 @@ function buildSongs(songs, filter) {
|
||||||
let ul = node.querySelector("ul");
|
let ul = node.querySelector("ul");
|
||||||
html.clear(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) {
|
function buildAlbums(albums, filter) {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import * as mpd from "./lib/mpd.js";
|
import * as mpd from "./lib/mpd.js";
|
||||||
import * as html from "./lib/html.js";
|
import * as html from "./lib/html.js";
|
||||||
import * as player from "./player.js";
|
|
||||||
import * as pubsub from "./lib/pubsub.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 node;
|
||||||
let currentId;
|
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) {
|
function buildSongs(songs) {
|
||||||
let ul = node.querySelector("ul");
|
let ul = node.querySelector("ul");
|
||||||
html.clear(ul);
|
html.clear(ul);
|
||||||
|
|
||||||
songs.map(buildSong).forEach(li => ul.appendChild(li));
|
songs.map(song => ui.song(ui.SONG_QUEUE, song, ul));
|
||||||
|
|
||||||
updateCurrent();
|
updateCurrent();
|
||||||
}
|
}
|
||||||
|
@ -57,12 +27,22 @@ function onSongChange(message, publisher, data) {
|
||||||
updateCurrent();
|
updateCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function activate() {
|
function onQueueChange(message, publisher, data) {
|
||||||
|
syncQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncQueue() {
|
||||||
let songs = await mpd.listQueue();
|
let songs = await mpd.listQueue();
|
||||||
buildSongs(songs);
|
buildSongs(songs);
|
||||||
|
// FIXME updateCount(songs.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function activate() {
|
||||||
|
syncQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init(n) {
|
export function init(n) {
|
||||||
node = n;
|
node = n;
|
||||||
pubsub.subscribe("song-change", onSongChange);
|
pubsub.subscribe("song-change", onSongChange);
|
||||||
|
pubsub.subscribe("queue-change", onQueueChange);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue