library back refactored

This commit is contained in:
Ondrej Zara 2020-03-14 20:33:41 +01:00
parent 0cd8ca8a55
commit dd64ba4d42
No known key found for this signature in database
GPG key ID: B0A5751E616840C5
3 changed files with 67 additions and 46 deletions

View file

@ -7,6 +7,7 @@ export default class Back extends Item {
super(); super();
this._title = title; this._title = title;
} }
connectedCallback() { connectedCallback() {
this.appendChild(html.icon("keyboard-backspace")); this.appendChild(html.icon("keyboard-backspace"));
this._buildTitle(this._title); this._buildTitle(this._title);

View file

@ -8,6 +8,10 @@ import { escape, serializeFilter } from "../mpd.js";
const SORT = "-Track"; const SORT = "-Track";
const TAGS = {
"Album": "Albums",
"AlbumArtist": "Artists"
}
function nonempty(str) { return (str.length > 0); } function nonempty(str) { return (str.length > 0); }
@ -30,9 +34,22 @@ function createEnqueueCommand(node) {
class Library extends Component { class Library extends Component {
constructor() { constructor() {
super({selection:"multi"}); super({selection:"multi"});
this._stateStack = [];
this._initCommands(); this._initCommands();
} }
_popState() {
this.selection.clear();
this._stateStack.pop();
if (this._stateStack.length > 0) {
let state = this._stateStack[this._stateStack.length-1];
this._showState(state);
} else {
this._showRoot();
}
}
_onAppLoad() { _onAppLoad() {
this._showRoot(); this._showRoot();
} }
@ -45,25 +62,40 @@ class Library extends Component {
} }
_showRoot() { _showRoot() {
this._stateStack = [];
html.clear(this); html.clear(this);
const nav = html.node("nav", {}, "", this); const nav = html.node("nav", {}, "", this);
html.button({icon:"artist"}, "Artists and albums", nav) html.button({icon:"artist"}, "Artists and albums", nav)
.addEventListener("click", _ => this._listTags("AlbumArtist")); .addEventListener("click", _ => this._pushState({type:"tags", tag:"AlbumArtist"}));
html.button({icon:"folder"}, "Files and directories", nav) html.button({icon:"folder"}, "Files and directories", nav)
.addEventListener("click", _ => this._listPath("")); .addEventListener("click", _ => this._pushState({type:"path", path:""}));
html.button({icon:"magnify"}, "Search", nav) html.button({icon:"magnify"}, "Search", nav)
.addEventListener("click", _ => this._showSearch()); .addEventListener("click", _ => this._pushState({type:"search"}));
}
_pushState(state) {
this._stateStack.push(state);
this._showState(state);
}
_showState(state) {
switch (state.type) {
case "tags": this._listTags(state.tag, state.filter); break;
case "songs": this._listSongs(state.filter); break;
case "path": this._listPath(state.path); break;
case "search": this._showSearch(state.query); break;
}
} }
async _listTags(tag, filter = {}) { async _listTags(tag, filter = {}) {
const values = await this._mpd.listTags(tag, filter); const values = await this._mpd.listTags(tag, filter);
html.clear(this); html.clear(this);
if ("AlbumArtist" in filter) { this._buildBack(filter); } if ("AlbumArtist" in filter) { this._buildBack(); }
values.filter(nonempty).forEach(value => this._buildTag(tag, value, filter)); values.filter(nonempty).forEach(value => this._buildTag(tag, value, filter));
} }
@ -71,7 +103,7 @@ class Library extends Component {
let paths = await this._mpd.listPath(path); let paths = await this._mpd.listPath(path);
html.clear(this); html.clear(this);
path && this._buildBack(path); path && this._buildBack();
paths["directory"].forEach(path => this._buildPath(path)); paths["directory"].forEach(path => this._buildPath(path));
paths["file"].forEach(path => this._buildPath(path)); paths["file"].forEach(path => this._buildPath(path));
} }
@ -79,30 +111,34 @@ class Library extends Component {
async _listSongs(filter) { async _listSongs(filter) {
const songs = await this._mpd.listSongs(filter); const songs = await this._mpd.listSongs(filter);
html.clear(this); html.clear(this);
this._buildBack(filter); this._buildBack();
songs.forEach(song => this.appendChild(new Song(song))); songs.forEach(song => this.appendChild(new Song(song)));
} }
_showSearch() { _showSearch(query = "") {
html.clear(this); html.clear(this);
const form = html.node("form", {}, "", this); const form = html.node("form", {}, "", this);
const input = html.node("input", {type:"text"}, "", form); const input = html.node("input", {type:"text", value:query}, "", form);
html.button({icon:"magnify"}, "", form); html.button({icon:"magnify"}, "", form);
form.addEventListener("submit", e => { form.addEventListener("submit", e => {
e.preventDefault(); e.preventDefault();
const q = input.value.trim(); const query = input.value.trim();
if (q.length < 3) { return; } if (query.length < 3) { return; }
this._doSearch(q, form); this._doSearch(query, form);
}); });
input.focus(); input.focus();
if (query) { this._doSearch(query, form); }
} }
async _doSearch(q, form) { async _doSearch(query, form) {
const songs1 = await this._mpd.searchSongs({"AlbumArtist": q}); let state = this._stateStack[this._stateStack.length-1];
const songs2 = await this._mpd.searchSongs({"Album": q}); state.query = query;
const songs3 = await this._mpd.searchSongs({"Title": q});
const songs1 = await this._mpd.searchSongs({"AlbumArtist": query});
const songs2 = await this._mpd.searchSongs({"Album": query});
const songs3 = await this._mpd.searchSongs({"Title": query});
html.clear(this); html.clear(this);
this.appendChild(form); this.appendChild(form);
@ -136,47 +172,29 @@ class Library extends Component {
case "AlbumArtist": case "AlbumArtist":
node = new Tag(tag, value, filter); node = new Tag(tag, value, filter);
this.appendChild(node); this.appendChild(node);
node.onClick = () => this._listTags("Album", node.createChildFilter()); node.onClick = () => this._pushState({type:"tags", tag:"Album", filter:node.createChildFilter()});
break; break;
case "Album": case "Album":
node = new Tag(tag, value, filter); node = new Tag(tag, value, filter);
this.appendChild(node); this.appendChild(node);
node.addButton("chevron-double-right", _ => this._listSongs(node.createChildFilter())); node.addButton("chevron-double-right", _ => this._pushState({type:"songs", filter:node.createChildFilter()}));
break; break;
} }
} }
_buildBack(filterOrPath) { _buildBack() {
if (typeof(filterOrPath) == "string") { const backState = this._stateStack[this._stateStack.length-2];
const path = filterOrPath.split("/").slice(0, -1).join(""); let title;
const node = new Back(".."); switch (backState.type) {
this.appendChild(node); case "path": title = ".."; break;
node.onClick = () => { case "search": title = "Search"; break;
this.selection.clear(); case "tags": title = TAGS[backState.tag]; break;
this._listPath(path);
}
return;
} }
const filter = Object.assign({}, filterOrPath)
let tag, title;
if ("Album" in filter) {
tag = "Album";
title = filter["AlbumArtist"];
} else if ("AlbumArtist" in filter) {
tag = "AlbumArtist";
title = "Artists";
}
delete filter[tag];
const node = new Back(title); const node = new Back(title);
this.appendChild(node); this.appendChild(node);
node.onClick = () => { node.onClick = () => this._popState();
this.selection.clear();
this._listTags(tag, filter);
}
} }
_buildPath(data) { _buildPath(data) {
@ -185,7 +203,7 @@ class Library extends Component {
if ("directory" in data) { if ("directory" in data) {
const path = data["directory"]; const path = data["directory"];
node.addButton("chevron-double-right", _ => this._listPath(path)); node.addButton("chevron-double-right", _ => this._pushState({type:"path", path}));
} }
} }

View file

@ -1,5 +1,8 @@
import * as html from "./html.js"; import * as html from "./html.js";
const TAGS = ["cyp-song", "cyp-tag", "cyp-path"];
export default class Selection { export default class Selection {
constructor(component, mode) { constructor(component, mode) {
this._component = component; this._component = component;
@ -25,8 +28,7 @@ export default class Selection {
addCommandAll() { addCommandAll() {
this.addCommand(_ => { this.addCommand(_ => {
Array.from(this._component.children) Array.from(this._component.querySelectorAll(TAGS.join(", ")))
.filter(node => node.tagName.toLowerCase().startsWith("cyp-"))
.forEach(node => this.add(node)); .forEach(node => this.add(node));
}, {label:"Select all", icon:"checkbox-marked-outline"}); }, {label:"Select all", icon:"checkbox-marked-outline"});
} }