jadefin/mods/ExtrasMenu.js

318 lines
10 KiB
JavaScript
Raw Normal View History

2024-03-01 21:01:22 +01:00
//@ts-check
import JadefinIntegrity from '../JadefinIntegrity.js';
2024-05-14 18:52:36 +02:00
import Jadefin from "../Jadefin.js";
2024-03-01 21:01:22 +01:00
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;
2024-03-01 21:01:22 +01:00
_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>
`;
2024-05-14 18:52:36 +02:00
drawerVersionEl = rd$()`
<div class="sidebarHeader extrasVersion">
Jellyfin <span id="extrasVersionJellyfin"></span><br>
Jadefin <span id="extrasVersionJadefin"></span>
</div>
`;
2024-03-01 21:01:22 +01:00
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()
]);
2024-03-01 21:01:22 +01:00
this.log.i("Ready");
}
async initHeaderExtras() {
await JadefinUtils.waitUntil(() => document.querySelector(".headerRight"));
2024-03-01 21:01:22 +01:00
document.querySelector(".headerRight")?.appendChild(this.headerExtrasEl);
}
/**
* @param {boolean} [silentWarn]
*/
async initDrawerExtras(silentWarn) {
2024-03-01 21:01:22 +01:00
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);
2024-05-14 18:52:36 +02:00
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;
}
2024-03-01 21:01:22 +01:00
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 >= 4 &&
options.items[0].id == "aspectratio" &&
options.items[1].id == "playbackrate" &&
options.items[2].id == "quality" &&
options.items[3].id == "repeatmode" &&
// suboffset might not always exist
// 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);
});
}
2024-03-01 21:01:22 +01:00
return orig(options);
};
}
2024-03-01 21:01:22 +01:00
openExtrasPopup() {
const currentVisibility = JadefinUtils.isInMovie ? this.IN_MOVIE : this.IN_LIBRARY;
2024-03-01 21:01:22 +01:00
this._actionSheetShow({
2024-03-01 21:01:22 +01:00
title: "Extras",
positionTo: this.headerExtrasEl,
currentVisibility,
items: this._items.map((item, i) => Object.assign({
id: i
}, item)).filter(item => this.checkVisibility(currentVisibility, item))
2024-03-01 21:01:22 +01:00
});
}
update() {
2024-05-14 18:52:36 +02:00
this.drawerExtrasEl.innerHTML = `<h3 class="sidebarHeader">Extras</h3>`;
2024-03-01 21:01:22 +01:00
2024-05-14 18:52:36 +02:00
for (let item of this._items) {
if (!this.checkVisibility(this.IN_DRAWER, item)) {
continue;
}
2024-03-01 21:01:22 +01:00
2024-05-14 18:52:36 +02:00
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>
`;
2024-03-01 21:01:22 +01:00
2024-05-14 18:52:36 +02:00
itemEl.addEventListener("click", e => item.cb?.());
2024-03-01 21:01:22 +01:00
2024-05-14 18:52:36 +02:00
item.cbEl?.(itemEl, this.IN_DRAWER);
2024-03-01 21:01:22 +01:00
2024-05-14 18:52:36 +02:00
this.drawerExtrasEl.appendChild(itemEl);
2024-03-01 21:01:22 +01:00
}
}
/**
* @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;
}
2024-03-01 21:01:22 +01:00
})());