Skip to content

Commit

Permalink
Add extension settings screen (#557)
Browse files Browse the repository at this point in the history
  • Loading branch information
schroda authored Jan 15, 2024
1 parent f0e1bd3 commit e10cbf9
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 100 deletions.
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import { LibrarySettings } from '@/screens/settings/LibrarySettings';
import { DefaultNavBar } from '@/components/navbar/DefaultNavBar';
import { DownloadSettings } from '@/screens/settings/DownloadSettings.tsx';
import { ServerSettings } from '@/screens/settings/ServerSettings.tsx';
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
import { ServerUpdateChecker } from '@/components/settings/ServerUpdateChecker.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { ExtensionSettings } from '@/screens/settings/ExtensionSettings.tsx';

if (process.env.NODE_ENV !== 'production') {
// Adds messages only in a dev environment
Expand Down Expand Up @@ -98,7 +98,7 @@ export const App: React.FC = () => (
<Route path="downloadSettings" element={<DownloadSettings />} />
<Route path="backup" element={<Backup />} />
<Route path="server" element={<ServerSettings />} />
<Route path="webUI" element={<WebUISettings />} />
<Route path="extensionSettings" element={<ExtensionSettings />} />
</Route>

{/* Manga Routes */}
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@
"title": "Extension repositories"
}
}
}
},
"title": "Extension Settings"
},
"state": {
"label": {
Expand Down
2 changes: 1 addition & 1 deletion src/screens/Extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export function Extensions() {
return (
<Stack sx={{ paddingTop: '20px' }} alignItems="center" justifyContent="center" rowGap="10px">
<Typography>{t('extension.label.add_repository_info')}</Typography>
<Button component={Link} variant="contained" to="/settings/server">
<Button component={Link} variant="contained" to="/settings/extensionSettings">
{t('settings.title')}
</Button>
</Stack>
Expand Down
19 changes: 7 additions & 12 deletions src/screens/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Switch from '@mui/material/Switch';
import FavoriteIcon from '@mui/icons-material/Favorite';
import { Link, ListItemButton, MenuItem, Select } from '@mui/material';
import { useTranslation } from 'react-i18next';
import LanguageIcon from '@mui/icons-material/Language';
Expand All @@ -27,6 +26,7 @@ import ViewModuleIcon from '@mui/icons-material/ViewModule';
import DnsIcon from '@mui/icons-material/Dns';
import WebIcon from '@mui/icons-material/Web';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import ExploreOutlinedIcon from '@mui/icons-material/ExploreOutlined';
import { langCodeToName } from '@/util/language';
import { useLocalStorage } from '@/util/useLocalStorage';
import { ListItemLink } from '@/components/util/ListItemLink';
Expand All @@ -46,7 +46,6 @@ export function Settings() {
}, [t]);

const { darkTheme, setDarkTheme } = useContext(DarkTheme);
const [showNsfw, setShowNsfw] = useLocalStorage<boolean>('showNsfw', true);

const DEFAULT_ITEM_WIDTH = 300;
const itemWidthIcon = useMemo(() => <ViewModuleIcon />, []);
Expand Down Expand Up @@ -116,16 +115,6 @@ export function Settings() {
showSlider
handleUpdate={setItemWidth}
/>
<ListItem>
<ListItemIcon>
<FavoriteIcon />
</ListItemIcon>
<ListItemText
primary={t('settings.label.show_nsfw')}
secondary={t('settings.label.show_nsfw_description')}
/>
<Switch edge="end" checked={showNsfw} onChange={() => setShowNsfw(!showNsfw)} />
</ListItem>
<ListItem>
<ListItemIcon>
<LanguageIcon />
Expand Down Expand Up @@ -162,6 +151,12 @@ export function Settings() {
secondary={t('settings.clear_cache.label.description')}
/>
</ListItemButton>
<ListItemLink to="/settings/extensionSettings">
<ListItemIcon>
<ExploreOutlinedIcon />
</ListItemIcon>
<ListItemText primary={t('global.label.browse')} />
</ListItemLink>
<ListItemLink to="/settings/webUI">
<ListItemIcon>
<WebIcon />
Expand Down
114 changes: 114 additions & 0 deletions src/screens/settings/ExtensionSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* 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 { Trans, useTranslation } from 'react-i18next';
import { useContext, useEffect } from 'react';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Switch from '@mui/material/Switch';
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { NumberSetting } from '@/components/settings/NumberSetting.tsx';
import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx';
import { ServerSettings as GqlServerSettings } from '@/typings.ts';
import { TextSetting } from '@/components/settings/text/TextSetting.tsx';
import { useLocalStorage } from '@/util/useLocalStorage.tsx';

type ExtensionsSettings = Pick<GqlServerSettings, 'maxSourcesInParallel' | 'localSourcePath' | 'extensionRepos'>;

const extractExtensionSettings = (settings: GqlServerSettings): ExtensionsSettings => ({
maxSourcesInParallel: settings.maxSourcesInParallel,
localSourcePath: settings.localSourcePath,
extensionRepos: settings.extensionRepos,
});

export const ExtensionSettings = () => {
const { t } = useTranslation();
const { setTitle, setAction } = useContext(NavBarContext);

useSetDefaultBackTo('settings');

useEffect(() => {
setTitle(t('extension.settings.title'));
setAction(null);
}, [t]);

const [showNsfw, setShowNsfw] = useLocalStorage<boolean>('showNsfw', true);

const { data } = requestManager.useGetServerSettings();
const serverSettings = data ? extractExtensionSettings(data.settings) : undefined;
const [mutateSettings] = requestManager.useUpdateServerSettings();

const updateSetting = <Setting extends keyof ExtensionsSettings>(
setting: Setting,
value: ExtensionsSettings[Setting],
) => {
mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
};

return (
<List>
<ListItem>
<ListItemText
primary={t('settings.label.show_nsfw')}
secondary={t('settings.label.show_nsfw_description')}
/>
<Switch edge="end" checked={showNsfw} onChange={() => setShowNsfw(!showNsfw)} />
</ListItem>
<NumberSetting
settingTitle={t('settings.server.requests.sources.parallel.label.title')}
settingValue={t('settings.server.requests.sources.parallel.label.value', {
value: serverSettings?.maxSourcesInParallel,
count: serverSettings?.maxSourcesInParallel,
})}
valueUnit={t('source.title')}
value={serverSettings?.maxSourcesInParallel ?? 6}
defaultValue={6}
minValue={1}
maxValue={20}
showSlider
stepSize={1}
dialogTitle={t('settings.server.requests.sources.parallel.label.title')}
handleUpdate={(parallelSources) => updateSetting('maxSourcesInParallel', parallelSources)}
/>
<MutableListSetting
settingName={t('extension.settings.repositories.custom.label.title')}
description={t('extension.settings.repositories.custom.label.description')}
dialogDisclaimer={
<Trans i18nKey="extension.settings.repositories.custom.label.disclaimer">
<strong>Suwayomi does not provide any support for 3rd party repositories or extensions!</strong>
<br />
Use with caution as there could be malicious actors making those repositories.
<br />
You as the user need to verify the security and that you trust any repository or extension.
</Trans>
}
handleChange={(repos) => {
updateSetting('extensionRepos', repos);
requestManager.clearExtensionCache();
}}
values={serverSettings?.extensionRepos}
addItemButtonTitle={t('extension.settings.repositories.custom.dialog.action.button.add')}
placeholder="https://github.com/MY_ACCOUNT/MY_REPO/tree/repo"
validateItem={(repo) =>
!!repo.match(
/https:\/\/(www\.|raw\.)?(github|githubusercontent)\.com\/([^/]+)\/([^/]+)((\/tree|\/blob)?\/([^/\n]*))?(\/([^/\n]*\.json)?)?/g,
)
}
invalidItemError={t('extension.settings.repositories.custom.error.label.invalid_url')}
/>
<TextSetting
settingName={t('settings.server.local_source.path.label.title')}
dialogDescription={t('settings.server.local_source.path.label.description')}
value={serverSettings?.localSourcePath}
handleChange={(path) => updateSetting('localSourcePath', path)}
/>
</List>
);
};
87 changes: 3 additions & 84 deletions src/screens/settings/ServerSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { useTranslation, Trans } from 'react-i18next';
import { useTranslation } from 'react-i18next';
import { useContext, useEffect } from 'react';
import { List, ListItem, ListItemText, Switch } from '@mui/material';
import ListSubheader from '@mui/material/ListSubheader';
Expand All @@ -16,7 +16,6 @@ import { useLocalStorage } from '@/util/useLocalStorage.tsx';
import { TextSetting } from '@/components/settings/text/TextSetting.tsx';
import { ServerSettings as GqlServerSettings } from '@/typings.ts';
import { NumberSetting } from '@/components/settings/NumberSetting.tsx';
import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx';

type ServerSettingsType = Pick<
GqlServerSettings,
Expand All @@ -31,12 +30,9 @@ type ServerSettingsType = Pick<
| 'basicAuthEnabled'
| 'basicAuthUsername'
| 'basicAuthPassword'
| 'maxSourcesInParallel'
| 'localSourcePath'
| 'extensionRepos'
>;

const extractDownloadSettings = (settings: GqlServerSettings): ServerSettingsType => ({
const extractServerSettings = (settings: GqlServerSettings): ServerSettingsType => ({
ip: settings.ip,
port: settings.port,
socksProxyEnabled: settings.socksProxyEnabled,
Expand All @@ -48,9 +44,6 @@ const extractDownloadSettings = (settings: GqlServerSettings): ServerSettingsTyp
basicAuthEnabled: settings.basicAuthEnabled,
basicAuthUsername: settings.basicAuthUsername,
basicAuthPassword: settings.basicAuthPassword,
maxSourcesInParallel: settings.maxSourcesInParallel,
localSourcePath: settings.localSourcePath,
extensionRepos: settings.extensionRepos,
});

export const ServerSettings = () => {
Expand All @@ -65,7 +58,7 @@ export const ServerSettings = () => {
}, [t]);

const { data } = requestManager.useGetServerSettings();
const serverSettings = data ? extractDownloadSettings(data.settings) : undefined;
const serverSettings = data ? extractServerSettings(data.settings) : undefined;
const [mutateSettings] = requestManager.useUpdateServerSettings();

const [serverAddress, setServerAddress] = useLocalStorage<string>('serverBaseURL', '');
Expand Down Expand Up @@ -99,80 +92,6 @@ export const ServerSettings = () => {
placeholder="http://localhost:4567"
/>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-requests">
{t('settings.server.requests.title')}
</ListSubheader>
}
>
<NumberSetting
settingTitle={t('settings.server.requests.sources.parallel.label.title')}
settingValue={t('settings.server.requests.sources.parallel.label.value', {
value: serverSettings?.maxSourcesInParallel,
count: serverSettings?.maxSourcesInParallel,
})}
valueUnit={t('source.title')}
value={serverSettings?.maxSourcesInParallel ?? 6}
defaultValue={6}
minValue={1}
maxValue={20}
showSlider
stepSize={1}
dialogTitle={t('settings.server.requests.sources.parallel.label.title')}
handleUpdate={(parallelSources) => updateSetting('maxSourcesInParallel', parallelSources)}
/>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-extension-repos">
{t('extension.title')}
</ListSubheader>
}
>
<MutableListSetting
settingName={t('extension.settings.repositories.custom.label.title')}
description={t('extension.settings.repositories.custom.label.description')}
dialogDisclaimer={
<Trans i18nKey="extension.settings.repositories.custom.label.disclaimer">
<strong>
Suwayomi does not provide any support for 3rd party repositories or extensions!
</strong>
<br />
Use with caution as there could be malicious actors making those repositories.
<br />
You as the user need to verify the security and that you trust any repository or extension.
</Trans>
}
handleChange={(repos) => {
updateSetting('extensionRepos', repos);
requestManager.clearExtensionCache();
}}
values={serverSettings?.extensionRepos}
addItemButtonTitle={t('extension.settings.repositories.custom.dialog.action.button.add')}
placeholder="https://github.com/MY_ACCOUNT/MY_REPO/tree/repo"
validateItem={(repo) =>
!!repo.match(
/https:\/\/(www\.|raw\.)?(github|githubusercontent)\.com\/([^/]+)\/([^/]+)((\/tree|\/blob)?\/([^/\n]*))?(\/([^/\n]*\.json)?)?/g,
)
}
invalidItemError={t('extension.settings.repositories.custom.error.label.invalid_url')}
/>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-requests">
{t('source.local_source.title')}
</ListSubheader>
}
>
<TextSetting
settingName={t('settings.server.local_source.path.label.title')}
dialogDescription={t('settings.server.local_source.path.label.description')}
value={serverSettings?.localSourcePath}
handleChange={(path) => updateSetting('localSourcePath', path)}
/>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-server-address">
Expand Down

0 comments on commit e10cbf9

Please sign in to comment.