From 2ba25c1a9b469db478e225cda9f1fa92b6051dc8 Mon Sep 17 00:00:00 2001 From: Mikal Stordal Date: Sun, 29 Sep 2024 12:03:19 +0200 Subject: [PATCH] fix: workaround for broken `await`/`async` support in Jellyfin Web To-be removed if the issue is fixed in the core, but for now we'll have this ugly workaround. --- Shokofin/Pages/Scripts/Common.js | 50 ++++++++++++++++++++++++++++-- Shokofin/Pages/Scripts/Dummy.js | 19 +++++++++--- Shokofin/Pages/Scripts/Settings.js | 23 ++++++++++---- 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/Shokofin/Pages/Scripts/Common.js b/Shokofin/Pages/Scripts/Common.js index 56083d82..ace81cd3 100644 --- a/Shokofin/Pages/Scripts/Common.js +++ b/Shokofin/Pages/Scripts/Common.js @@ -737,9 +737,11 @@ const Messages = { * @param {HTMLDivElement} view - The view element. * @param {ViewLifecycleEvents} events - The events. * @param {TabType} [initialTab] - The initial tab. + * @param {boolean} [hide] - Whether to hide the view immediately. + * @param {boolean} [show] - Whether to show the view immediately. * @returns {void} Void. */ -export function setupEvents(view, events, initialTab = "connection") { +export function setupEvents(view, events, initialTab = "connection", hide = false, show = false) { if (events.onBeforeShow) { view.addEventListener("viewbeforeshow", events.onBeforeShow.bind(view)); } @@ -835,6 +837,46 @@ export function setupEvents(view, events, initialTab = "connection") { const initEvent = new CustomEvent("viewinit", { detail: {}, bubbles: true, cancelable: false }); events.onInit.call(view, initEvent); + + // Do nothing if both show and hide are requested. + if (hide && show) return; + + // Show the view if requested. + if (show) { + const eventDetails = { + /** @type {FullDetails} */ + detail: { + type: view.getAttribute("data-type") || null, + params: Object.fromEntries(new URLSearchParams(window.location.hash.split("#").slice(1).join("#").split("?").slice(1).join("?"))), + properties: (view.getAttribute("data-properties") || "").split(","), + isRestored: undefined, + state: null, + options: { + supportsThemeMedia: false, + enableMediaControls: true, + }, + }, + bubbles: true, + cancelable: false, + } + view.dispatchEvent(new CustomEvent("viewbeforeshow", eventDetails)); + view.dispatchEvent(new CustomEvent("viewshow", eventDetails)); + } + + // Hide the view if requested. + if (hide) { + const eventDetails = { + /** @type {MinimalDetails} */ + detail: { + type: event.detail.type, + properties: event.detail.properties, + }, + bubbles: true, + cancelable: false, + }; + view.dispatchEvent(new CustomEvent("viewbeforehide", { ...eventDetails, cancelable: true })); + view.dispatchEvent(new CustomEvent("viewhide", eventDetails)); + } } } @@ -857,6 +899,8 @@ export function setupEvents(view, events, initialTab = "connection") { * @typedef {Object} controllerFactoryOptions * @property {ViewLifecycleEvents} events The lifecycle events for the view. * @property {TabType} [initialTab] - The initial tab. + * @property {boolean} [show] - Whether to show the view immediately. + * @property {boolean} [hide] - Whether to hide the view immediately. */ /** @@ -866,9 +910,9 @@ export function setupEvents(view, events, initialTab = "connection") { * @returns {controllerFactoryFn} The controller factory. */ export function createControllerFactory(options) { - const { events, initialTab } = options; + const { events, initialTab, hide, show } = options; return function(view) { - setupEvents(view, events, initialTab); + setupEvents(view, events, initialTab, hide, show); } } diff --git a/Shokofin/Pages/Scripts/Dummy.js b/Shokofin/Pages/Scripts/Dummy.js index 53daf80a..f08dcff6 100644 --- a/Shokofin/Pages/Scripts/Dummy.js +++ b/Shokofin/Pages/Scripts/Dummy.js @@ -1,3 +1,9 @@ +export default function (view) { +let show = false; +let hide = false; +view.addEventListener("viewshow", () => show = true); +view.addEventListener("viewhide", () => hide = true); + /** * @type {import("./Common.js").ApiClientPrototype} */ @@ -9,11 +15,14 @@ const ApiClient = globalThis.ApiClient; const Dashboard = globalThis.Dashboard; /** - * @type {import("./Common.js")} + * @type {Promise} */ -const { State, createControllerFactory } = await import(ApiClient.getUrl("/web/" + Dashboard.getPluginUrl("Shoko.Common.js"))); +const promise = import(ApiClient.getUrl("/web/" + Dashboard.getPluginUrl("Shoko.Common.js"))); +promise.then(({ State, createControllerFactory }) => { -export default createControllerFactory({ +createControllerFactory({ + show, + hide, initialTab: "utilities", events: { onShow(event) { @@ -35,4 +44,6 @@ export default createControllerFactory({ content.innerHTML = "Dummy."; }, }, -}); +})(view); + +}); } diff --git a/Shokofin/Pages/Scripts/Settings.js b/Shokofin/Pages/Scripts/Settings.js index b90a342c..e295e489 100644 --- a/Shokofin/Pages/Scripts/Settings.js +++ b/Shokofin/Pages/Scripts/Settings.js @@ -1,3 +1,9 @@ +export default function (view) { +let show = false; +let hide = false; +view.addEventListener("viewshow", () => show = true); +view.addEventListener("viewhide", () => hide = true); + /** * @type {import("./Common.js").ApiClientPrototype} */ @@ -9,9 +15,10 @@ const ApiClient = globalThis.ApiClient; const Dashboard = globalThis.Dashboard; /** - * @type {import("./Common.js")} + * @type {Promise} */ -const { +const promise = import(ApiClient.getUrl("/web/" + Dashboard.getPluginUrl("Shoko.Common.js"))); +promise.then(({ ShokoApiClient, State, createControllerFactory, @@ -22,7 +29,7 @@ const { retrieveCheckboxList, retrieveSortableCheckboxList, updateTabs, -} = await import(ApiClient.getUrl("/web/" + Dashboard.getPluginUrl("Shoko.Common.js"))); +}) => { //#region Constants @@ -72,7 +79,9 @@ const Messages = { //#region Controller Logic -export default createControllerFactory({ +createControllerFactory({ + show, + hide, events: { onInit() { const view = this; @@ -263,7 +272,7 @@ export default createControllerFactory({ applyFormToConfig(form, State.config); }, } -}); +})(view); /** * Update the view to reflect the current state. @@ -1221,4 +1230,6 @@ function filterReconnectIntervals(value) { return Array.from(filteredSet).sort((a, b) => a - b); } -//#endregion \ No newline at end of file +//#endregion + +}); } \ No newline at end of file