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();
this._title = title;
}
connectedCallback() {
this.appendChild(html.icon("keyboard-backspace"));
this._buildTitle(this._title);

View file

@ -8,6 +8,10 @@ import { escape, serializeFilter } from "../mpd.js";
const SORT = "-Track";
const TAGS = {
"Album": "Albums",
"AlbumArtist": "Artists"
}
function nonempty(str) { return (str.length > 0); }
@ -30,9 +34,22 @@ function createEnqueueCommand(node) {
class Library extends Component {
constructor() {
super({selection:"multi"});
this._stateStack = [];
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() {
this._showRoot();
}
@ -45,25 +62,40 @@ class Library extends Component {
}
_showRoot() {
this._stateStack = [];
html.clear(this);
const nav = html.node("nav", {}, "", this);
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)
.addEventListener("click", _ => this._listPath(""));
.addEventListener("click", _ => this._pushState({type:"path", path:""}));
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 = {}) {
const values = await this._mpd.listTags(tag, filter);
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));
}
@ -71,7 +103,7 @@ class Library extends Component {
let paths = await this._mpd.listPath(path);
html.clear(this);
path && this._buildBack(path);
path && this._buildBack();
paths["directory"].forEach(path => this._buildPath(path));
paths["file"].forEach(path => this._buildPath(path));
}
@ -79,30 +111,34 @@ class Library extends Component {
async _listSongs(filter) {
const songs = await this._mpd.listSongs(filter);
html.clear(this);
this._buildBack(filter);
this._buildBack();
songs.forEach(song => this.appendChild(new Song(song)));
}
_showSearch() {
_showSearch(query = "") {
html.clear(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);
form.addEventListener("submit", e => {
e.preventDefault();
const q = input.value.trim();
if (q.length < 3) { return; }
this._doSearch(q, form);
const query = input.value.trim();
if (query.length < 3) { return; }
this._doSearch(query, form);
});
input.focus();
if (query) { this._doSearch(query, form); }
}
async _doSearch(q, form) {
const songs1 = await this._mpd.searchSongs({"AlbumArtist": q});
const songs2 = await this._mpd.searchSongs({"Album": q});
const songs3 = await this._mpd.searchSongs({"Title": q});
async _doSearch(query, form) {
let state = this._stateStack[this._stateStack.length-1];
state.query = query;
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);
this.appendChild(form);
@ -136,47 +172,29 @@ class Library extends Component {
case "AlbumArtist":
node = new Tag(tag, value, filter);
this.appendChild(node);
node.onClick = () => this._listTags("Album", node.createChildFilter());
node.onClick = () => this._pushState({type:"tags", tag:"Album", filter:node.createChildFilter()});
break;
case "Album":
node = new Tag(tag, value, filter);
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;
}
}
_buildBack(filterOrPath) {
if (typeof(filterOrPath) == "string") {
const path = filterOrPath.split("/").slice(0, -1).join("");
const node = new Back("..");
this.appendChild(node);
node.onClick = () => {
this.selection.clear();
this._listPath(path);
}
return;
_buildBack() {
const backState = this._stateStack[this._stateStack.length-2];
let title;
switch (backState.type) {
case "path": title = ".."; break;
case "search": title = "Search"; break;
case "tags": title = TAGS[backState.tag]; break;
}
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);
this.appendChild(node);
node.onClick = () => {
this.selection.clear();
this._listTags(tag, filter);
}
node.onClick = () => this._popState();
}
_buildPath(data) {
@ -185,7 +203,7 @@ class Library extends Component {
if ("directory" in data) {
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";
const TAGS = ["cyp-song", "cyp-tag", "cyp-path"];
export default class Selection {
constructor(component, mode) {
this._component = component;
@ -25,8 +28,7 @@ export default class Selection {
addCommandAll() {
this.addCommand(_ => {
Array.from(this._component.children)
.filter(node => node.tagName.toLowerCase().startsWith("cyp-"))
Array.from(this._component.querySelectorAll(TAGS.join(", ")))
.forEach(node => this.add(node));
}, {label:"Select all", icon:"checkbox-marked-outline"});
}