diff --git a/src/App.tsx b/src/App.tsx
index c833a21b..3ee40643 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -24,6 +24,7 @@ import Settings from 'screens/Settings';
import About from 'screens/settings/About';
import Backup from 'screens/settings/Backup';
import Categories from 'screens/settings/Categories';
+import DefaultReaderSettings from 'screens/settings/DefaultReaderSettings';
import SourceConfigure from 'screens/SourceConfigure';
import SourceMangas from 'screens/SourceMangas';
import Sources from 'screens/Sources';
@@ -60,6 +61,9 @@ const App: React.FC = () => (
+
+
+
diff --git a/src/components/navbar/ReaderNavBar.tsx b/src/components/navbar/ReaderNavBar.tsx
index 20600754..71e59d4b 100644
--- a/src/components/navbar/ReaderNavBar.tsx
+++ b/src/components/navbar/ReaderNavBar.tsx
@@ -17,18 +17,15 @@ import { useHistory, Link, useLocation } from 'react-router-dom';
import Slide from '@mui/material/Slide';
import Fade from '@mui/material/Fade';
import Zoom from '@mui/material/Zoom';
-import { Divider, Switch } from '@mui/material';
-import List from '@mui/material/List';
+import { Divider } from '@mui/material';
import ListItem from '@mui/material/ListItem';
-import MenuItem from '@mui/material/MenuItem';
-import Select from '@mui/material/Select';
import ListItemText from '@mui/material/ListItemText';
import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
import Collapse from '@mui/material/Collapse';
import Button from '@mui/material/Button';
import { styled } from '@mui/system';
import useBackTo from 'util/useBackTo';
-import { getMetadataFrom } from 'util/metadata';
+import ReaderSettingsOptions from '../reader/ReaderSettingsOptions';
const Root = styled('div')(({ theme }) => ({
top: 0,
@@ -106,27 +103,6 @@ const OpenDrawerButton = styled(IconButton)(({ theme }) => ({
},
}));
-const defaultReaderSettings = () => ({
- staticNav: false,
- showPageNumber: true,
- continuesPageGap: false,
- loadNextonEnding: false,
- readerType: 'ContinuesVertical',
-} as IReaderSettings);
-
-const getReaderSettingsFromMetadata = (
- meta?: IMetadata,
-): IReaderSettings => ({
- ...getMetadataFrom(
- { meta },
- Object.entries(defaultReaderSettings()) as MetadataKeyValuePair[],
- ) as unknown as IReaderSettings,
-});
-
-export const getReaderSettingsFor = (
- { meta }: IMangaCard | IManga,
-): IReaderSettings => getReaderSettingsFromMetadata(meta);
-
interface IProps {
settings: IReaderSettings
setSettingValue: (key: keyof IReaderSettings, value: string | boolean) => void
@@ -276,75 +252,13 @@ export default function ReaderNavBar(props: IProps) {
-
-
-
-
- updateSettingValue('staticNav', e.target.checked)}
- />
-
-
-
-
-
- updateSettingValue('showPageNumber', e.target.checked)}
- />
-
-
-
-
-
- updateSettingValue('loadNextonEnding', e.target.checked)}
- />
-
-
-
-
-
-
-
+
diff --git a/src/components/reader/ReaderSettingsOptions.tsx b/src/components/reader/ReaderSettingsOptions.tsx
new file mode 100644
index 00000000..12197b83
--- /dev/null
+++ b/src/components/reader/ReaderSettingsOptions.tsx
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) Contributors to the Suwayomi project
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+import {
+ List, ListItem, ListItemText, Switch,
+} from '@mui/material';
+import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
+import Select from '@mui/material/Select';
+import MenuItem from '@mui/material/MenuItem';
+import React from 'react';
+
+interface IProps extends IReaderSettings {
+ setSettingValue: (key: keyof IReaderSettings, value: string | boolean) => void
+}
+
+export default function ReaderSettingsOptions({
+ staticNav, loadNextonEnding, readerType, showPageNumber, setSettingValue,
+}: IProps) {
+ return (
+ <>
+
+
+
+
+ setSettingValue('staticNav', e.target.checked)}
+ />
+
+
+
+
+
+ setSettingValue('showPageNumber', e.target.checked)}
+ />
+
+
+
+
+
+ setSettingValue('loadNextonEnding', e.target.checked)}
+ />
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx
index 33663c02..4c2cef0c 100644
--- a/src/screens/Reader.tsx
+++ b/src/screens/Reader.tsx
@@ -15,12 +15,17 @@ import PageNumber from 'components/reader/PageNumber';
import PagedPager from 'components/reader/pager/PagedPager';
import DoublePagedPager from 'components/reader/pager/DoublePagedPager';
import VerticalPager from 'components/reader/pager/VerticalPager';
-import ReaderNavBar, { getReaderSettingsFor } from 'components/navbar/ReaderNavBar';
+import ReaderNavBar from 'components/navbar/ReaderNavBar';
import NavbarContext from 'components/context/NavbarContext';
import client from 'util/client';
import useLocalStorage from 'util/useLocalStorage';
import { Box } from '@mui/system';
import { requestUpdateMangaMetadata } from 'util/metadata';
+import {
+ checkAndHandleMissingStoredReaderSettings,
+ getReaderSettingsFor,
+ useDefaultReaderSettings,
+} from 'util/readerSettings';
import makeToast from '../components/util/Toast';
const getReaderComponent = (readerType: ReaderType) => {
@@ -68,13 +73,25 @@ export default function Reader() {
const [curPage, setCurPage] = useState(0);
const { setOverride, setTitle } = useContext(NavbarContext);
- const [settings, setSettings] = useState(getReaderSettingsFor(manga));
+ const {
+ settings: defaultSettings,
+ loading: areDefaultSettingsLoading,
+ } = useDefaultReaderSettings();
+ const [settings, setSettings] = useState(getReaderSettingsFor(manga, defaultSettings));
+ const [isMangaLoading, setIsMangaLoading] = useState(true);
const setSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
setSettings({ ...settings, [key]: value });
requestUpdateMangaMetadata(manga, [[key, value]]).catch(() => makeToast('Failed to save the reader settings to the server', 'warning'));
};
+ useEffect(() => {
+ if (!areDefaultSettingsLoading && !isMangaLoading) {
+ checkAndHandleMissingStoredReaderSettings(manga, 'manga', defaultSettings).catch(() => {});
+ setSettings(getReaderSettingsFor(manga, defaultSettings));
+ }
+ }, [areDefaultSettingsLoading, isMangaLoading]);
+
useEffect(() => {
// set the custom navbar
setOverride(
@@ -97,13 +114,14 @@ export default function Reader() {
}, [manga, chapter, settings, curPage, chapterIndex]);
useEffect(() => {
+ setIsMangaLoading(true);
setTitle('Reader');
client.get(`/api/v1/manga/${mangaId}/`)
.then((response) => response.data)
.then((data: IManga) => {
setManga(data);
setTitle(data.title);
- setSettings(getReaderSettingsFor(data));
+ setIsMangaLoading(false);
});
}, [mangaId]);
diff --git a/src/screens/Settings.tsx b/src/screens/Settings.tsx
index 50dd2303..3f976e1f 100644
--- a/src/screens/Settings.tsx
+++ b/src/screens/Settings.tsx
@@ -6,6 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import React, { useContext, useEffect, useState } from 'react';
+import AutoStoriesIcon from '@mui/icons-material/AutoStories';
import List from '@mui/material/List';
import ListAltIcon from '@mui/icons-material/ListAlt';
import BackupIcon from '@mui/icons-material/Backup';
@@ -96,6 +97,12 @@ export default function Settings() {
+
+
+
+
+
+
diff --git a/src/screens/settings/DefaultReaderSettings.tsx b/src/screens/settings/DefaultReaderSettings.tsx
new file mode 100644
index 00000000..d8110a70
--- /dev/null
+++ b/src/screens/settings/DefaultReaderSettings.tsx
@@ -0,0 +1,60 @@
+/* eslint-disable @typescript-eslint/no-shadow */
+/* eslint-disable react/destructuring-assignment */
+/*
+ * Copyright (C) Contributors to the Suwayomi project
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+import React, { useContext, useEffect } from 'react';
+import NavbarContext from 'components/context/NavbarContext';
+import { Box } from '@mui/system';
+import CircularProgress from '@mui/material/CircularProgress';
+import { requestUpdateServerMetadata } from 'util/metadata';
+import makeToast from 'components/util/Toast';
+import {
+ checkAndHandleMissingStoredReaderSettings,
+ getDefaultSettings,
+ useDefaultReaderSettings,
+} from 'util/readerSettings';
+import ReaderSettingsOptions from 'components/reader/ReaderSettingsOptions';
+
+export default function DefaultReaderSettings() {
+ const { setTitle, setAction } = useContext(NavbarContext);
+ useEffect(() => {
+ setTitle('Default Reader Settings');
+ setAction(<>>);
+ }, []);
+
+ const { metadata, settings, loading } = useDefaultReaderSettings();
+
+ const setSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
+ requestUpdateServerMetadata(metadata ?? {}, [[key, value]]).catch(() => makeToast('Failed to save the default reader settings to the server', 'warning'));
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ checkAndHandleMissingStoredReaderSettings({ meta: metadata }, 'server', getDefaultSettings())
+ .catch(() => {});
+
+ return (
+
+ );
+}
diff --git a/src/util/readerSettings.ts b/src/util/readerSettings.ts
new file mode 100644
index 00000000..37004b9d
--- /dev/null
+++ b/src/util/readerSettings.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) Contributors to the Suwayomi project
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+import { getMetadataFrom, requestUpdateMangaMetadata, requestUpdateServerMetadata } from 'util/metadata';
+import { useQuery } from './client';
+
+export const getDefaultSettings = (forceUndefined: boolean = false) => ({
+ staticNav: forceUndefined ? undefined : false,
+ showPageNumber: forceUndefined ? undefined : true,
+ continuesPageGap: forceUndefined ? undefined : false,
+ loadNextonEnding: forceUndefined ? undefined : false,
+ readerType: forceUndefined ? undefined : 'ContinuesVertical',
+} as IReaderSettings);
+
+const getReaderSettingsWithDefaultValueFallback = (
+ meta?: IMetadata,
+ defaultSettings?: IReaderSettings,
+): IReaderSettings => ({
+ ...getMetadataFrom(
+ { meta },
+ Object.entries(defaultSettings ?? getDefaultSettings()) as MetadataKeyValuePair[],
+ ) as unknown as IReaderSettings,
+});
+
+export const getReaderSettingsFromMetadata = (
+ meta?: IMetadata,
+ defaultSettings?: IReaderSettings,
+): IReaderSettings => ({
+ ...getReaderSettingsWithDefaultValueFallback(meta, defaultSettings),
+});
+
+export const getReaderSettingsFor = (
+ { meta }: IMetadataHolder,
+ defaultSettings?: IReaderSettings,
+): IReaderSettings => getReaderSettingsFromMetadata(meta, defaultSettings);
+
+export const useDefaultReaderSettings = (): {
+ metadata?: IMetadata,
+ settings: IReaderSettings,
+ loading: boolean
+} => {
+ const { data: meta, loading } = useQuery('/api/v1/meta');
+ const settings = getReaderSettingsWithDefaultValueFallback(meta);
+
+ return { metadata: meta, settings, loading };
+};
+
+/**
+ * Saves all missing reader settings from the metadata to the server
+ *
+ * @param metadataHolder
+ * @param metadataHolderType
+ * @param defaultSettings
+ */
+export const checkAndHandleMissingStoredReaderSettings = async (
+ metadataHolder: IManga | IMetadataHolder,
+ metadataHolderType: 'manga' | 'server',
+ defaultSettings: IReaderSettings,
+): Promise => {
+ const meta = metadataHolder.meta ?? metadataHolder as IMetadata;
+ const settingsToCheck = getReaderSettingsFor({ meta }, getDefaultSettings(true));
+ const newSettings = getReaderSettingsFor({ meta }, defaultSettings);
+
+ const undefinedSettings = Object.entries(settingsToCheck)
+ .filter((setting) => setting[1] === undefined);
+
+ const settingsToUpdate: MetadataKeyValuePair[] = [];
+ undefinedSettings.forEach((setting) => {
+ const key = setting[0] as keyof IReaderSettings;
+
+ settingsToUpdate.push([key, newSettings[key]]);
+ });
+
+ if (!undefinedSettings.length) {
+ return;
+ }
+
+ if (metadataHolderType === 'manga') {
+ await requestUpdateMangaMetadata(metadataHolder as IManga, settingsToUpdate);
+ return;
+ }
+
+ await requestUpdateServerMetadata(meta, settingsToUpdate);
+};