diff --git a/src/RootAppRouter.tsx b/src/RootAppRouter.tsx index 9c2205d339..bb64480816 100644 --- a/src/RootAppRouter.tsx +++ b/src/RootAppRouter.tsx @@ -13,13 +13,16 @@ import { STABLE_APP_ROUTES } from 'apps/stable/routes/routes'; import { WIZARD_APP_ROUTES } from 'apps/wizard/routes/routes'; import AppHeader from 'components/AppHeader'; import Backdrop from 'components/Backdrop'; +import { SETTING_KEY as LAYOUT_SETTING_KEY } from 'components/layoutManager'; import BangRedirect from 'components/router/BangRedirect'; import { createRouterHistory } from 'components/router/routerHistory'; +import { LayoutMode } from 'constants/layoutMode'; +import browser from 'scripts/browser'; import appTheme from 'themes/themes'; import { ThemeStorageManager } from 'themes/themeStorageManager'; -const layoutMode = localStorage.getItem('layout'); -const isExperimentalLayout = layoutMode === 'experimental'; +const layoutMode = browser.tv ? LayoutMode.Tv : localStorage.getItem(LAYOUT_SETTING_KEY); +const isExperimentalLayout = !layoutMode || layoutMode === LayoutMode.Experimental; const router = createHashRouter([ { diff --git a/src/apps/experimental/features/preferences/components/DisplayPreferences.tsx b/src/apps/experimental/features/preferences/components/DisplayPreferences.tsx index b26f21d111..0668ccdbbe 100644 --- a/src/apps/experimental/features/preferences/components/DisplayPreferences.tsx +++ b/src/apps/experimental/features/preferences/components/DisplayPreferences.tsx @@ -12,6 +12,7 @@ import React, { Fragment } from 'react'; import { appHost } from 'components/apphost'; import { AppFeature } from 'constants/appFeature'; +import { LayoutMode } from 'constants/layoutMode'; import { useApi } from 'hooks/useApi'; import { useThemes } from 'hooks/useThemes'; import globalize from 'lib/globalize'; @@ -45,11 +46,10 @@ export function DisplayPreferences({ onChange, values }: Readonly - {globalize.translate('Auto')} - {globalize.translate('Desktop')} - {globalize.translate('Mobile')} - {globalize.translate('TV')} - {globalize.translate('Experimental')} + {globalize.translate('Auto')} + {globalize.translate('Desktop')} + {globalize.translate('Mobile')} + {globalize.translate('TV')} {globalize.translate('DisplayModeHelp')} diff --git a/src/components/apphost.js b/src/components/apphost.js index ce88435a50..8b9436021e 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -6,6 +6,7 @@ import * as webSettings from '../scripts/settings/webSettings'; import globalize from '../lib/globalize'; import profileBuilder from '../scripts/browserDeviceProfile'; import { AppFeature } from 'constants/appFeature'; +import { LayoutMode } from 'constants/layoutMode'; const appName = 'Jellyfin Web'; @@ -181,7 +182,7 @@ function supportsFullscreen() { } function getDefaultLayout() { - return 'desktop'; + return LayoutMode.Experimental; } function supportsHtmlMediaAutoplay() { @@ -371,7 +372,7 @@ export const appHost = { return getDefaultLayout(); }, - getDeviceProfile: getDeviceProfile, + getDeviceProfile, init: function () { if (window.NativeShell) { return window.NativeShell.AppHost.init(); diff --git a/src/components/displaySettings/displaySettings.template.html b/src/components/displaySettings/displaySettings.template.html index 6b219906c6..edaf266221 100644 --- a/src/components/displaySettings/displaySettings.template.html +++ b/src/components/displaySettings/displaySettings.template.html @@ -172,7 +172,6 @@ -
${DisplayModeHelp}
${LabelPleaseRestart}
diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 563d92af3a..abc0aef4fe 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -1,3 +1,4 @@ +import { LayoutMode } from 'constants/layoutMode'; import { appHost } from './apphost'; import browser from '../scripts/browser'; @@ -14,51 +15,47 @@ function setLayout(instance, layout, selectedLayout) { } } +export const SETTING_KEY = 'layout'; + class LayoutManager { tv = false; mobile = false; desktop = false; experimental = false; - setLayout(layout, save) { - if (!layout || layout === 'auto') { + setLayout(layout = '', save = true) { + const layoutValue = (!layout || layout === LayoutMode.Auto) ? '' : layout; + + if (!layoutValue) { this.autoLayout(); - - if (save !== false) { - appSettings.set('layout', ''); - } } else { - setLayout(this, 'mobile', layout); - setLayout(this, 'tv', layout); - setLayout(this, 'desktop', layout); - - this.experimental = layout === 'experimental'; - if (this.experimental) { - const legacyLayoutMode = browser.mobile ? 'mobile' : this.defaultLayout || 'desktop'; - setLayout(this, legacyLayoutMode, legacyLayoutMode); - } - - if (save !== false) { - appSettings.set('layout', layout); - } + setLayout(this, LayoutMode.Mobile, layoutValue); + setLayout(this, LayoutMode.Tv, layoutValue); + setLayout(this, LayoutMode.Desktop, layoutValue); } + console.debug('[LayoutManager] using layout mode', layoutValue); + this.experimental = layoutValue === LayoutMode.Experimental; + if (this.experimental) { + const legacyLayoutMode = browser.mobile ? LayoutMode.Mobile : LayoutMode.Desktop; + console.debug('[LayoutManager] using legacy layout mode', legacyLayoutMode); + setLayout(this, legacyLayoutMode, legacyLayoutMode); + } + + if (save) appSettings.set(SETTING_KEY, layoutValue); + Events.trigger(this, 'modechange'); } getSavedLayout() { - return appSettings.get('layout'); + return appSettings.get(SETTING_KEY); } autoLayout() { - // Take a guess at initial layout. The consuming app can override - if (browser.mobile) { - this.setLayout('mobile', false); - } else if (browser.tv || browser.xboxOne || browser.ps4) { - this.setLayout('tv', false); - } else { - this.setLayout(this.defaultLayout || 'tv', false); - } + // Take a guess at initial layout. The consuming app can override. + // NOTE: The fallback to TV mode seems like an outdated choice. TVs should be detected properly or override the + // default layout. + this.setLayout(browser.tv ? LayoutMode.Tv : this.defaultLayout || LayoutMode.Tv, false); } init() { diff --git a/src/components/router/appRouter.js b/src/components/router/appRouter.js index 032ddfaf1e..8d90531fd8 100644 --- a/src/components/router/appRouter.js +++ b/src/components/router/appRouter.js @@ -6,6 +6,7 @@ import itemHelper from '../itemHelper'; import loading from '../loading/loading'; import alert from '../alert'; +import { LayoutMode } from 'constants/layoutMode'; import { getItemQuery } from 'hooks/useItem'; import { ServerConnections } from 'lib/jellyfin-apiclient'; import { toApi } from 'utils/jellyfin-apiclient/compat'; @@ -434,7 +435,7 @@ class AppRouter { const layoutMode = localStorage.getItem('layout'); - if (layoutMode === 'experimental' && item.CollectionType == CollectionType.Homevideos) { + if (layoutMode === LayoutMode.Experimental && item.CollectionType == CollectionType.Homevideos) { url = '#/homevideos?topParentId=' + item.Id; return url; diff --git a/src/constants/layoutMode.ts b/src/constants/layoutMode.ts new file mode 100644 index 0000000000..1d5a311dff --- /dev/null +++ b/src/constants/layoutMode.ts @@ -0,0 +1,13 @@ +/** The different layout modes supported by the web app. */ +export enum LayoutMode { + /** Automatic layout - the app chose the best layout for the detected device. */ + Auto = 'auto', + /** The legacy desktop layout. */ + Desktop = 'desktop', + /** The modern React based layout. */ + Experimental = 'experimental', + /** The legacy mobile layout. */ + Mobile = 'mobile', + /** The TV layout. */ + Tv = 'tv' +}; diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 099fefac10..6075274e7e 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -232,7 +232,7 @@ "DeleteServerConfirmation": "Are you sure you wish to delete this server?", "Depressed": "Depressed", "Descending": "Descending", - "Desktop": "Desktop", + "Desktop": "Desktop (Legacy)", "DetectingDevices": "Detecting devices", "DeviceAccessHelp": "This only applies to devices that can be uniquely identified and will not prevent browser access. Filtering user device access will prevent them from using new devices until they've been approved here.", "Digital": "Digital", @@ -332,7 +332,6 @@ "EveryXHours": "Every {0} hours", "EveryXMinutes": "Every {0} minutes", "ExitFullscreen": "Exit full screen", - "Experimental": "Experimental", "ExtractChapterImagesHelp": "Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, resource intensive, and may require several gigabytes of space. It runs when videos are discovered, and also as a nightly scheduled task. The schedule is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours.", "ExtraLarge": "Extra Large", "Extras": "Extras", @@ -1217,7 +1216,7 @@ "MinutesBefore": "minutes before", "MixedMoviesShows": "Mixed Movies and Shows", "Mixer": "Mixer", - "Mobile": "Mobile", + "Mobile": "Mobile (Legacy)", "Monday": "Monday", "MoreFromValue": "More from {0}", "MoreMediaInfo": "Media Info",