diff --git a/src/apps/experimental/components/AppToolbar/menus/RemotePlayActiveMenu.tsx b/src/apps/experimental/components/AppToolbar/menus/RemotePlayActiveMenu.tsx index 0ea518c6c9..e184a3a25d 100644 --- a/src/apps/experimental/components/AppToolbar/menus/RemotePlayActiveMenu.tsx +++ b/src/apps/experimental/components/AppToolbar/menus/RemotePlayActiveMenu.tsx @@ -12,7 +12,7 @@ import { playbackManager } from 'components/playback/playbackmanager'; import React, { FC, useCallback, useState } from 'react'; import { Link } from 'react-router-dom'; -import { enable, isEnabled, supported } from 'scripts/autocast'; +import { enable, isEnabled } from 'scripts/autocast'; import globalize from 'scripts/globalize'; interface RemotePlayActiveMenuProps extends MenuProps { @@ -43,11 +43,10 @@ const RemotePlayActiveMenu: FC = ({ }, [ isDisplayMirrorEnabled, setIsDisplayMirrorEnabled ]); const [ isAutoCastEnabled, setIsAutoCastEnabled ] = useState(isEnabled()); - const isAutoCastSupported = supported(); const toggleAutoCast = useCallback(() => { enable(!isAutoCastEnabled); setIsAutoCastEnabled(!isAutoCastEnabled); - }, [ isAutoCastEnabled, setIsAutoCastEnabled ]); + }, [ isAutoCastEnabled ]); const remotePlayerName = playerInfo?.deviceName || playerInfo?.name; @@ -117,20 +116,18 @@ const RemotePlayActiveMenu: FC = ({ )} - {isAutoCastSupported && ( - - {isAutoCastEnabled && ( - - - - )} - - {globalize.translate('EnableAutoCast')} - - - )} + + {isAutoCastEnabled && ( + + + + )} + + {globalize.translate('EnableAutoCast')} + + - {(isDisplayMirrorSupported || isAutoCastSupported) && } + redirect('/home.html') }, + { index: true, element: }, ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute) ] }, diff --git a/src/apps/stable/routes/routes.tsx b/src/apps/stable/routes/routes.tsx index bf66a48459..61feac2ad0 100644 --- a/src/apps/stable/routes/routes.tsx +++ b/src/apps/stable/routes/routes.tsx @@ -1,4 +1,4 @@ -import { RouteObject, redirect } from 'react-router-dom'; +import { Navigate, RouteObject } from 'react-router-dom'; import React from 'react'; import ConnectionRequired from 'components/ConnectionRequired'; @@ -30,7 +30,7 @@ export const STABLE_APP_ROUTES: RouteObject[] = [ }, /* Public routes */ - { index: true, loader: () => redirect('/home.html') }, + { index: true, element: }, ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute) ] }, diff --git a/src/components/ConnectionRequired.tsx b/src/components/ConnectionRequired.tsx index 8dde8ec1e7..ad2577c5d1 100644 --- a/src/components/ConnectionRequired.tsx +++ b/src/components/ConnectionRequired.tsx @@ -83,7 +83,7 @@ const ConnectionRequired: FunctionComponent = ({ if (firstConnection.State === ConnectionState.ServerSignIn) { // Verify the wizard is complete try { - const infoResponse = await fetch(`${firstConnection.ApiClient.serverAddress()}/System/Info/Public`); + const infoResponse = await fetch(`${firstConnection.ApiClient.serverAddress()}/System/Info/Public`, { cache: 'no-cache' }); if (!infoResponse.ok) { throw new Error('Public system info request failed'); } diff --git a/src/components/playback/playerSelectionMenu.js b/src/components/playback/playerSelectionMenu.js index c4a1c6dd80..1e57b30442 100644 --- a/src/components/playback/playerSelectionMenu.js +++ b/src/components/playback/playerSelectionMenu.js @@ -6,7 +6,7 @@ import { pluginManager } from '../pluginManager'; import { appRouter } from '../router/appRouter'; import globalize from '../../scripts/globalize'; import { appHost } from '../apphost'; -import { enable, isEnabled, supported } from '../../scripts/autocast'; +import { enable, isEnabled } from '../../scripts/autocast'; import '../../elements/emby-checkbox/emby-checkbox'; import '../../elements/emby-button/emby-button'; import dialog from '../dialog/dialog'; @@ -200,13 +200,11 @@ function showActivePlayerMenuInternal(playerInfo) { html += ''; - if (supported()) { - html += '
'; - } + html += '
'; html += '
'; diff --git a/src/components/router/appRouter.js b/src/components/router/appRouter.js index 20571c840f..31e949035d 100644 --- a/src/components/router/appRouter.js +++ b/src/components/router/appRouter.js @@ -387,7 +387,7 @@ class AppRouter { if (firstResult) { if (firstResult.State === ConnectionState.ServerSignIn) { const url = firstResult.ApiClient.serverAddress() + '/System/Info/Public'; - fetch(url).then(response => { + fetch(url, { cache: 'no-cache' }).then(response => { if (!response.ok) return Promise.reject(new Error('fetch failed')); return response.json(); }).then(data => { diff --git a/src/hooks/useSystemInfo.ts b/src/hooks/useSystemInfo.ts index 9fed3dd3f6..0fa4604a59 100644 --- a/src/hooks/useSystemInfo.ts +++ b/src/hooks/useSystemInfo.ts @@ -23,7 +23,7 @@ export const getSystemInfoQuery = ( api?: Api ) => queryOptions({ queryKey: [ 'SystemInfo' ], - queryFn: ({ signal }) => fetchSystemInfo(api, { signal }), + queryFn: ({ signal }) => fetchSystemInfo(api, { signal, headers: { 'Cache-Control': 'no-cache' } }), // Allow for query reuse in legacy javascript. staleTime: 1000, // 1 second enabled: !!api diff --git a/src/index.jsx b/src/index.jsx index a74802cbb2..e8a3b5a3ad 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -24,6 +24,7 @@ import packageManager from './components/packageManager'; import './components/playback/displayMirrorManager.ts'; import { appRouter } from './components/router/appRouter'; import './elements/emby-button/emby-button'; +import { initialize as initializeAutoCast } from 'scripts/autocast'; import './scripts/autoThemes'; import './components/themeMediaPlayer'; import { pageClassOn, serverAddress } from './utils/dashboard'; @@ -79,6 +80,8 @@ build: ${__JF_BUILD_VERSION__}`); }).then(() => { console.debug('initAfterDependencies promises resolved'); + initializeAutoCast(ServerConnections.currentApiClient()); + loadCoreDictionary().then(function () { onGlobalizeInit(); }); diff --git a/src/plugins/bookPlayer/plugin.js b/src/plugins/bookPlayer/plugin.js index ef8fef28b5..17fec59285 100644 --- a/src/plugins/bookPlayer/plugin.js +++ b/src/plugins/bookPlayer/plugin.js @@ -7,6 +7,7 @@ import ServerConnections from '../../components/ServerConnections'; import Screenfull from 'screenfull'; import TableOfContents from './tableOfContents'; import { translateHtml } from '../../scripts/globalize'; +import browser from 'scripts/browser'; import * as userSettings from '../../scripts/settings/userSettings'; import TouchHelper from 'scripts/touchHelper'; import { PluginType } from '../../types/plugin.ts'; @@ -45,6 +46,7 @@ export class BookPlayer { this.previous = this.previous.bind(this); this.next = this.next.bind(this); this.onWindowKeyUp = this.onWindowKeyUp.bind(this); + this.addSwipeGestures = this.addSwipeGestures.bind(this); } play(options) { @@ -155,6 +157,12 @@ export class BookPlayer { } } + addSwipeGestures(element) { + this.touchHelper = new TouchHelper(element); + Events.on(this.touchHelper, 'swipeleft', () => this.next()); + Events.on(this.touchHelper, 'swiperight', () => this.previous()); + } + onDialogClosed() { this.stop(); } @@ -179,10 +187,12 @@ export class BookPlayer { document.addEventListener('keyup', this.onWindowKeyUp); this.rendition?.on('keyup', this.onWindowKeyUp); - const player = document.getElementById('bookPlayerContainer'); - this.touchHelper = new TouchHelper(player); - Events.on(this.touchHelper, 'swipeleft', () => this.next()); - Events.on(this.touchHelper, 'swiperight', () => this.previous()); + if (browser.safari) { + const player = document.getElementById('bookPlayerContainer'); + this.addSwipeGestures(player); + } else { + this.rendition?.on('rendered', (e, i) => this.addSwipeGestures(i.document.documentElement)); + } } unbindMediaElementEvents() { @@ -207,6 +217,10 @@ export class BookPlayer { document.removeEventListener('keyup', this.onWindowKeyUp); this.rendition?.off('keyup', this.onWindowKeyUp); + if (!browser.safari) { + this.rendition?.off('rendered', (e, i) => this.addSwipeGestures(i.document.documentElement)); + } + this.touchHelper?.destroy(); } diff --git a/src/scripts/autocast.js b/src/scripts/autocast.js index 2633587ba5..ead25e06eb 100644 --- a/src/scripts/autocast.js +++ b/src/scripts/autocast.js @@ -1,14 +1,7 @@ import { playbackManager } from '../components/playback/playbackmanager'; -import ServerConnections from '../components/ServerConnections'; import Events from '../utils/events.ts'; -export function supported() { - return typeof(Storage) !== 'undefined'; -} - export function enable(enabled) { - if (!supported()) return; - if (enabled) { const currentPlayerInfo = playbackManager.getPlayerInfo(); @@ -21,8 +14,6 @@ export function enable(enabled) { } export function isEnabled() { - if (!supported()) return false; - const playerId = localStorage.getItem('autocastPlayerId'); const currentPlayerInfo = playbackManager.getPlayerInfo(); @@ -42,12 +33,10 @@ function onOpen() { }); } -try { - const apiClient = ServerConnections.currentApiClient(); - - if (apiClient && supported()) { +export function initialize(apiClient) { + if (apiClient) { Events.on(apiClient, 'websocketopen', onOpen); + } else { + console.warn('[autoCast] cannot initialize missing apiClient'); } -} catch (ex) { - console.warn('Could not get current apiClient', ex); } diff --git a/src/scripts/multiDownload.js b/src/scripts/multiDownload.js index ec3db52015..c9f40f56a4 100644 --- a/src/scripts/multiDownload.js +++ b/src/scripts/multiDownload.js @@ -27,19 +27,11 @@ function fallback(urls) { })(); } -function sameDomain(url) { - const a = document.createElement('a'); - a.href = url; - - return window.location.hostname === a.hostname && window.location.protocol === a.protocol; -} - function download(url) { const a = document.createElement('a'); a.download = ''; a.href = url; - // firefox doesn't support `a.click()`... - a.dispatchEvent(new MouseEvent('click')); + a.click(); } export default function (urls) { @@ -47,19 +39,13 @@ export default function (urls) { throw new Error('`urls` required'); } - if (typeof document.createElement('a').download === 'undefined') { + if (typeof document.createElement('a').download === 'undefined' || browser.iOS) { return fallback(urls); } let delay = 0; urls.forEach(function (url) { - // the download init has to be sequential for firefox if the urls are not on the same domain - if (browser.firefox && !sameDomain(url)) { - setTimeout(download.bind(null, url), 100 * ++delay); - return; - } - - download(url); + setTimeout(download.bind(null, url), 100 * ++delay); }); } diff --git a/src/utils/dashboard.js b/src/utils/dashboard.js index 393125c6fd..89c4c406d6 100644 --- a/src/utils/dashboard.js +++ b/src/utils/dashboard.js @@ -52,7 +52,7 @@ export async function serverAddress() { console.debug('URL candidates:', urls); const promises = urls.map(url => { - return fetch(`${url}/System/Info/Public`) + return fetch(`${url}/System/Info/Public`, { cache: 'no-cache' }) .then(async resp => { if (!resp.ok) { return;