Fix/save active device only in local storage (#643)
* Save "activeDevice" in the local storage * Set default back to for "DeviceSetting"
This commit is contained in:
26
src/App.tsx
26
src/App.tsx
@@ -6,9 +6,9 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Box, Container } from '@mui/material';
|
import { Container } from '@mui/material';
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
import React, { useEffect, useLayoutEffect, useMemo } from 'react';
|
import React, { useLayoutEffect } from 'react';
|
||||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
|
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
|
||||||
import { AppContext } from '@/components/context/AppContext';
|
import { AppContext } from '@/components/context/AppContext';
|
||||||
@@ -38,8 +38,6 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
|
|||||||
import { BrowseSettings } from '@/screens/settings/BrowseSettings.tsx';
|
import { BrowseSettings } from '@/screens/settings/BrowseSettings.tsx';
|
||||||
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
|
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
|
||||||
import { Migrate } from '@/screens/Migrate.tsx';
|
import { Migrate } from '@/screens/Migrate.tsx';
|
||||||
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
|
||||||
import { getActiveDevice, setActiveDevice } from '@/util/device.ts';
|
|
||||||
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
@@ -72,27 +70,8 @@ const BackgroundSubscriptions = () => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActiveDeviceListener = ({ children }: { children?: React.ReactNode }) => {
|
|
||||||
const {
|
|
||||||
settings: { devices, activeDevice },
|
|
||||||
} = useMetadataServerSettings();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeDevice === getActiveDevice()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setActiveDevice(activeDevice);
|
|
||||||
}, [devices, activeDevice]);
|
|
||||||
|
|
||||||
const memorizedChildren = useMemo(() => children, [activeDevice]);
|
|
||||||
|
|
||||||
return <Box key={activeDevice}>{memorizedChildren}</Box>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const App: React.FC = () => (
|
export const App: React.FC = () => (
|
||||||
<AppContext>
|
<AppContext>
|
||||||
<ActiveDeviceListener>
|
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
<ServerUpdateChecker />
|
<ServerUpdateChecker />
|
||||||
<BackgroundSubscriptions />
|
<BackgroundSubscriptions />
|
||||||
@@ -154,6 +133,5 @@ export const App: React.FC = () => (
|
|||||||
<Route path="manga/:mangaId/chapter/:chapterIndex" element={<Reader />} />
|
<Route path="manga/:mangaId/chapter/:chapterIndex" element={<Reader />} />
|
||||||
<Route path="*" element={null} />
|
<Route path="*" element={null} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</ActiveDeviceListener>
|
|
||||||
</AppContext>
|
</AppContext>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useLocalStorage } from '@/util/useLocalStorage';
|
|||||||
import { DarkTheme } from '@/components/context/DarkTheme';
|
import { DarkTheme } from '@/components/context/DarkTheme';
|
||||||
import { NavBarContextProvider } from '@/components/navbar/NavBarContextProvider';
|
import { NavBarContextProvider } from '@/components/navbar/NavBarContextProvider';
|
||||||
import { LibraryOptionsContextProvider } from '@/components/library/LibraryOptionsProvider';
|
import { LibraryOptionsContextProvider } from '@/components/library/LibraryOptionsProvider';
|
||||||
|
import { ActiveDevice, DEFAULT_DEVICE, setActiveDevice } from '@/util/device.ts';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -23,6 +24,7 @@ interface Props {
|
|||||||
|
|
||||||
export const AppContext: React.FC<Props> = ({ children }) => {
|
export const AppContext: React.FC<Props> = ({ children }) => {
|
||||||
const [darkTheme, setDarkTheme] = useLocalStorage<boolean>('darkTheme', true);
|
const [darkTheme, setDarkTheme] = useLocalStorage<boolean>('darkTheme', true);
|
||||||
|
const [activeDevice, setActiveDeviceContext] = useLocalStorage('activeDevice', DEFAULT_DEVICE);
|
||||||
|
|
||||||
const darkThemeContext = useMemo(
|
const darkThemeContext = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -32,8 +34,15 @@ export const AppContext: React.FC<Props> = ({ children }) => {
|
|||||||
[darkTheme],
|
[darkTheme],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const activeDeviceContext = useMemo(
|
||||||
|
() => ({ activeDevice, setActiveDevice: setActiveDeviceContext }),
|
||||||
|
[activeDevice],
|
||||||
|
);
|
||||||
|
|
||||||
const theme = useMemo(() => createTheme(darkTheme), [darkTheme]);
|
const theme = useMemo(() => createTheme(darkTheme), [darkTheme]);
|
||||||
|
|
||||||
|
setActiveDevice(activeDevice);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
@@ -41,7 +50,11 @@ export const AppContext: React.FC<Props> = ({ children }) => {
|
|||||||
<DarkTheme.Provider value={darkThemeContext}>
|
<DarkTheme.Provider value={darkThemeContext}>
|
||||||
<QueryParamProvider adapter={ReactRouter6Adapter}>
|
<QueryParamProvider adapter={ReactRouter6Adapter}>
|
||||||
<LibraryOptionsContextProvider>
|
<LibraryOptionsContextProvider>
|
||||||
<NavBarContextProvider>{children}</NavBarContextProvider>
|
<NavBarContextProvider>
|
||||||
|
<ActiveDevice.Provider value={activeDeviceContext}>
|
||||||
|
{children}
|
||||||
|
</ActiveDevice.Provider>
|
||||||
|
</NavBarContextProvider>
|
||||||
</LibraryOptionsContextProvider>
|
</LibraryOptionsContextProvider>
|
||||||
</QueryParamProvider>
|
</QueryParamProvider>
|
||||||
</DarkTheme.Provider>
|
</DarkTheme.Provider>
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ import { MetadataServerSettingKeys, MetadataServerSettings } from '@/typings.ts'
|
|||||||
import { convertToGqlMeta, requestUpdateServerMetadata } from '@/util/metadata.ts';
|
import { convertToGqlMeta, requestUpdateServerMetadata } from '@/util/metadata.ts';
|
||||||
import { makeToast } from '@/components/util/Toast.tsx';
|
import { makeToast } from '@/components/util/Toast.tsx';
|
||||||
import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx';
|
import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx';
|
||||||
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
||||||
import { DEFAULT_DEVICE } from '@/util/device.ts';
|
import { ActiveDevice, DEFAULT_DEVICE } from '@/util/device.ts';
|
||||||
|
|
||||||
export const DeviceSetting = () => {
|
export const DeviceSetting = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setTitle, setAction } = useContext(NavBarContext);
|
const { setTitle, setAction } = useContext(NavBarContext);
|
||||||
|
|
||||||
|
useSetDefaultBackTo('settings');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTitle(t('settings.device.title.settings'));
|
setTitle(t('settings.device.title.settings'));
|
||||||
setAction(null);
|
setAction(null);
|
||||||
@@ -33,10 +35,12 @@ export const DeviceSetting = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
metadata,
|
metadata,
|
||||||
settings: { devices, activeDevice },
|
settings: { devices },
|
||||||
} = useMetadataServerSettings();
|
} = useMetadataServerSettings();
|
||||||
|
|
||||||
const updateMetadataSetting = async <Setting extends MetadataServerSettingKeys>(
|
const { activeDevice, setActiveDevice } = useContext(ActiveDevice);
|
||||||
|
|
||||||
|
const updateMetadataSetting = <Setting extends MetadataServerSettingKeys>(
|
||||||
setting: Setting,
|
setting: Setting,
|
||||||
value: MetadataServerSettings[Setting],
|
value: MetadataServerSettings[Setting],
|
||||||
) => {
|
) => {
|
||||||
@@ -46,14 +50,7 @@ export const DeviceSetting = () => {
|
|||||||
|
|
||||||
const wasActiveDeviceDeleted = setting === 'devices' && !(value as string[]).includes(activeDevice);
|
const wasActiveDeviceDeleted = setting === 'devices' && !(value as string[]).includes(activeDevice);
|
||||||
if (wasActiveDeviceDeleted) {
|
if (wasActiveDeviceDeleted) {
|
||||||
try {
|
setActiveDevice(DEFAULT_DEVICE);
|
||||||
await requestUpdateServerMetadata(convertToGqlMeta(metadata) ?? [], [
|
|
||||||
['activeDevice', convertSettingsToMetadata({ activeDevice: DEFAULT_DEVICE }).activeDevice],
|
|
||||||
]);
|
|
||||||
} catch (e) {
|
|
||||||
makeToast(t('global.error.label.failed_to_save_changes'), 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestUpdateServerMetadata(convertToGqlMeta(metadata) ?? [], [
|
requestUpdateServerMetadata(convertToGqlMeta(metadata) ?? [], [
|
||||||
@@ -82,7 +79,7 @@ export const DeviceSetting = () => {
|
|||||||
<Select
|
<Select
|
||||||
MenuProps={{ PaperProps: { style: { maxHeight: 150 } } }}
|
MenuProps={{ PaperProps: { style: { maxHeight: 150 } } }}
|
||||||
value={activeDevice}
|
value={activeDevice}
|
||||||
onChange={({ target: { value: device } }) => updateMetadataSetting('activeDevice', device)}
|
onChange={({ target: { value: device } }) => setActiveDevice(device)}
|
||||||
>
|
>
|
||||||
{devices.map((device) => (
|
{devices.map((device) => (
|
||||||
<MenuItem key={device} value={device}>
|
<MenuItem key={device} value={device}>
|
||||||
|
|||||||
@@ -232,7 +232,6 @@ export type MetadataServerSettings = {
|
|||||||
|
|
||||||
// client
|
// client
|
||||||
devices: string[];
|
devices: string[];
|
||||||
activeDevice: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ISearchSettings {
|
export interface ISearchSettings {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
export const DEFAULT_DEVICE = 'default';
|
export const DEFAULT_DEVICE = 'default';
|
||||||
|
|
||||||
let activeDevice = DEFAULT_DEVICE;
|
let activeDevice = DEFAULT_DEVICE;
|
||||||
@@ -13,3 +15,8 @@ export const getActiveDevice = (): string => activeDevice;
|
|||||||
export const setActiveDevice = (device: string) => {
|
export const setActiveDevice = (device: string) => {
|
||||||
activeDevice = device;
|
activeDevice = device;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ActiveDevice = React.createContext<{ activeDevice: string; setActiveDevice: (device: string) => void }>({
|
||||||
|
activeDevice: DEFAULT_DEVICE,
|
||||||
|
setActiveDevice: () => {},
|
||||||
|
});
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
|
|||||||
|
|
||||||
// client
|
// client
|
||||||
'devices',
|
'devices',
|
||||||
'activeDevice',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
|
|||||||
|
|
||||||
// client
|
// client
|
||||||
devices: [DEFAULT_DEVICE],
|
devices: [DEFAULT_DEVICE],
|
||||||
activeDevice: DEFAULT_DEVICE,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const convertSettingsToMetadata = (
|
export const convertSettingsToMetadata = (
|
||||||
|
|||||||
Reference in New Issue
Block a user