317 lines
10 KiB
JavaScript
317 lines
10 KiB
JavaScript
//@ts-check
|
|
|
|
import JadefinIntegrity from '../JadefinIntegrity.js';
|
|
|
|
import Jadefin from "../Jadefin.js";
|
|
import JadefinMod from "../JadefinMod.js";
|
|
import JadefinModules from "../JadefinModules.js";
|
|
import JadefinUtils from "../JadefinUtils.js";
|
|
|
|
import { rd, rdom, rd$, RDOMListHelper } from "../utils/rdom.js";
|
|
|
|
export default JadefinIntegrity("ExtrasMenu", import.meta.url, () => new (class ExtrasMenu extends JadefinMod {
|
|
IN_ALL = 0;
|
|
IN_CUSTOM = 1;
|
|
IN_DRAWER = 2;
|
|
IN_LIBRARY = 4;
|
|
IN_MOVIE = 8;
|
|
IN_PLAYBACKSETTINGS = 16;
|
|
IN_PLAYBACKSETTINGS_ADVANCED = 32;
|
|
|
|
_popupId = 0;
|
|
_items = [];
|
|
|
|
/** @type {typeof this._items} */
|
|
items = new Proxy(this._items, {
|
|
deleteProperty: (target, property) => {
|
|
delete target[property];
|
|
|
|
this.update();
|
|
|
|
return true;
|
|
},
|
|
|
|
set: (target, property, value, receiver) => {
|
|
target[property] = value;
|
|
|
|
this.update();
|
|
|
|
return true;
|
|
}
|
|
});
|
|
|
|
headerExtrasEl = rd$()`
|
|
<button is="paper-icon-button-light" class="headerExtrasMenu headerButton headerButtonRight" title="Extras" style="display: inline-flex;">
|
|
<span class="material-icons tune"></span>
|
|
</button>
|
|
`;
|
|
|
|
drawerExtrasEl = rd$()`
|
|
<div class="extrasMenuOptions">
|
|
</div>
|
|
`;
|
|
|
|
drawerVersionEl = rd$()`
|
|
<div class="sidebarHeader extrasVersion">
|
|
Jellyfin <span id="extrasVersionJellyfin"></span><br>
|
|
Jadefin <span id="extrasVersionJadefin"></span>
|
|
</div>
|
|
`;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
this.items.push({
|
|
name: "Reload",
|
|
secondaryText: "Fully reload and update Jellyfin",
|
|
icon: "update",
|
|
in: this.IN_LIBRARY,
|
|
cb: async () => {
|
|
try {
|
|
await window.caches.delete("embydata");
|
|
} catch (e) {
|
|
}
|
|
try {
|
|
await window.caches.delete(`workbox-precache-v2-${JadefinModules.Emby.Page.baseRoute}/`);
|
|
} catch (e) {
|
|
}
|
|
window.location.reload();
|
|
}
|
|
});
|
|
|
|
this.headerExtrasEl.addEventListener("click", e => this.openExtrasPopup());
|
|
}
|
|
|
|
async init(name, url) {
|
|
await super.init(name, url);
|
|
|
|
await Promise.all([
|
|
this.initStyle(),
|
|
this.initHeaderExtras(),
|
|
this.initDrawerExtras(),
|
|
this.initHookActionSheetShow()
|
|
]);
|
|
|
|
this.log.i("Ready");
|
|
}
|
|
|
|
async initHeaderExtras() {
|
|
await JadefinUtils.waitUntil(() => document.querySelector(".headerRight"));
|
|
document.querySelector(".headerRight")?.appendChild(this.headerExtrasEl);
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} [silentWarn]
|
|
*/
|
|
async initDrawerExtras(silentWarn) {
|
|
const drawer = document.querySelector(".mainDrawer > .mainDrawer-scrollContainer");
|
|
const userMenuOptions = drawer?.querySelector("& > .userMenuOptions");
|
|
|
|
if (!userMenuOptions) {
|
|
if (!silentWarn) {
|
|
this.log.w("Couldn't find mainDrawer / home userMenuOptions, retrying");
|
|
}
|
|
|
|
setTimeout(() => this.initDrawerExtras(true), 1500);
|
|
|
|
return;
|
|
}
|
|
|
|
drawer?.insertBefore(this.drawerExtrasEl, userMenuOptions);
|
|
drawer?.appendChild(this.drawerVersionEl);
|
|
|
|
const versionJellyfinEl = this.drawerVersionEl.querySelector("#extrasVersionJellyfin");
|
|
if (versionJellyfinEl) {
|
|
versionJellyfinEl.textContent = JadefinModules.ApiClient.appVersion();
|
|
}
|
|
|
|
const versionJadefinEl = this.drawerVersionEl.querySelector("#extrasVersionJadefin");
|
|
if (versionJadefinEl) {
|
|
versionJadefinEl.textContent = Jadefin.version;
|
|
}
|
|
|
|
this.update();
|
|
}
|
|
|
|
async initHookActionSheetShow() {
|
|
await JadefinUtils.waitUntil(() => JadefinModules.actionSheet);
|
|
|
|
const orig = this._actionSheetShowOrig = JadefinModules.actionSheet.show.bind(JadefinModules.actionSheet);
|
|
JadefinModules.actionSheet.show = (options) => {
|
|
const optionsOrig = Object.assign({}, options, {
|
|
items: options.items.slice()
|
|
});
|
|
|
|
// Options menu during playback
|
|
if (!options.dialogClass &&
|
|
options.items.length >= 6 &&
|
|
options.items[0].id == "aspectratio" &&
|
|
options.items[1].id == "playbackrate" &&
|
|
options.items[2].id == "quality" &&
|
|
options.items[3].id == "repeatmode" &&
|
|
options.items[4].id == "suboffset" &&
|
|
options.items[5].id == "stats" &&
|
|
true
|
|
) {
|
|
return new Promise((resolve, reject) => {
|
|
const currentVisibility = this.IN_PLAYBACKSETTINGS;
|
|
const items = [
|
|
{id: "extrasMenuPopup-all", name: "Advanced..."},
|
|
{divider: true},
|
|
...this._items.map((item, i) => Object.assign({
|
|
id: i
|
|
}, item)).filter(item => this.checkVisibility(currentVisibility, item)),
|
|
...optionsOrig.items.slice(4)
|
|
];
|
|
|
|
const p = this._actionSheetShow({
|
|
positionTo: optionsOrig.positionTo,
|
|
currentVisibility,
|
|
items
|
|
});
|
|
|
|
p.then(id => {
|
|
if (id == "extrasMenuPopup-all") {
|
|
const currentVisibility = this.IN_PLAYBACKSETTINGS_ADVANCED;
|
|
const items = [
|
|
...optionsOrig.items.slice(0, 4),
|
|
...this._items.map((item, i) => Object.assign({
|
|
id: i
|
|
}, item)).filter(item => this.checkVisibility(currentVisibility, item)),
|
|
{divider: true},
|
|
{id: "extrasMenuPopup-back", name: "Back..."}
|
|
];
|
|
|
|
const p = this._actionSheetShow(Object.assign({}, optionsOrig, {
|
|
currentVisibility,
|
|
items
|
|
}));
|
|
|
|
p.then(id => {
|
|
if (id == "extrasMenuPopup-back") {
|
|
JadefinModules.actionSheet.show(optionsOrig).then(resolve).catch(reject);
|
|
return;
|
|
}
|
|
|
|
if (id && this._items[id]) {
|
|
reject();
|
|
return;
|
|
}
|
|
|
|
resolve(id);
|
|
}).catch(reject);
|
|
return;
|
|
}
|
|
|
|
if (id && this._items[id]) {
|
|
reject();
|
|
return;
|
|
}
|
|
|
|
resolve(id);
|
|
}).catch(reject);
|
|
});
|
|
}
|
|
|
|
return orig(options);
|
|
};
|
|
}
|
|
|
|
openExtrasPopup() {
|
|
const currentVisibility = JadefinUtils.isInMovie ? this.IN_MOVIE : this.IN_LIBRARY;
|
|
|
|
this._actionSheetShow({
|
|
title: "Extras",
|
|
positionTo: this.headerExtrasEl,
|
|
currentVisibility,
|
|
items: this._items.map((item, i) => Object.assign({
|
|
id: i
|
|
}, item)).filter(item => this.checkVisibility(currentVisibility, item))
|
|
});
|
|
}
|
|
|
|
update() {
|
|
this.drawerExtrasEl.innerHTML = `<h3 class="sidebarHeader">Extras</h3>`;
|
|
|
|
for (let item of this._items) {
|
|
if (!this.checkVisibility(this.IN_DRAWER, item)) {
|
|
continue;
|
|
}
|
|
|
|
let itemEl = rd$()`
|
|
<a is="emby-linkbutton" class="navMenuOption emby-button" href="#">
|
|
<span class=${`material-icons navMenuOptionIcon ${item.icon}`} aria-hidden="true"></span>
|
|
<div class="navMenuOptionTextBlock">
|
|
<span class="navMenuOptionText">${item.name}</span><br>
|
|
<span class="navMenuOptionTextSubtext">${item.secondaryText}</span>
|
|
</div>
|
|
</a>
|
|
`;
|
|
|
|
itemEl.addEventListener("click", e => item.cb?.());
|
|
|
|
item.cbEl?.(itemEl, this.IN_DRAWER);
|
|
|
|
this.drawerExtrasEl.appendChild(itemEl);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} current
|
|
* @param {any} item
|
|
*/
|
|
checkVisibility(current, item) {
|
|
if ((item.in || this.IN_ALL) == this.IN_ALL) {
|
|
return true;
|
|
}
|
|
|
|
if ((item.in & this.IN_CUSTOM) == this.IN_CUSTOM) {
|
|
return item.inCustom(current, item);
|
|
}
|
|
|
|
return (item.in & current) == current;
|
|
}
|
|
|
|
/**
|
|
* @param {{ positionTo: any; currentVisibility: any; items: any; title?: string; dialogClass?: any; }} options
|
|
*/
|
|
_actionSheetShow(options) {
|
|
options.dialogClass = `extrasMenuPopup-${this._popupId++}`;
|
|
|
|
const p = JadefinModules.actionSheet.show(options);
|
|
|
|
p.then(id => {
|
|
if (!id) {
|
|
return;
|
|
}
|
|
|
|
this._items[id]?.cb?.(options.positionTo);
|
|
});
|
|
|
|
const dialogEl = document.querySelector(`.${options.dialogClass}`);
|
|
if (!dialogEl) {
|
|
this.log.e(`Couldn't find .${options.dialogClass}`);
|
|
return p;
|
|
}
|
|
|
|
this.log.i(`Opened .${options.dialogClass}`);
|
|
this.log.dir(dialogEl);
|
|
|
|
dialogEl.classList.add("extrasMenuPopup");
|
|
|
|
const dialogButtons = dialogEl.querySelectorAll("button");
|
|
|
|
for (let i in options.items) {
|
|
options.items[i].cbEl?.(dialogButtons[i], options.currentVisibility, true);
|
|
}
|
|
|
|
p.finally(() => {
|
|
for (let i in options.items) {
|
|
options.items[i].cbEl?.(dialogButtons[i], options.currentVisibility, false);
|
|
}
|
|
});
|
|
|
|
return p;
|
|
}
|
|
})());
|