diff --git a/src/components/manga/sourceConfiguration/CheckBoxPreference.tsx b/src/components/manga/sourceConfiguration/CheckBoxPreference.tsx
deleted file mode 100644
index 043dcff5..00000000
--- a/src/components/manga/sourceConfiguration/CheckBoxPreference.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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 ListItem from '@mui/material/ListItem';
-import ListItemText from '@mui/material/ListItemText';
-import React from 'react';
-
-export default function CheckBoxPreference({ title, summary }: CheckBoxPreferenceProps) {
- return (
-
-
-
- );
-}
diff --git a/src/components/manga/sourceConfiguration/EditTextPreference.tsx b/src/components/manga/sourceConfiguration/EditTextPreference.tsx
new file mode 100644
index 00000000..38a6f053
--- /dev/null
+++ b/src/components/manga/sourceConfiguration/EditTextPreference.tsx
@@ -0,0 +1,80 @@
+/*
+ * 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, { useState } from 'react';
+import ListItem from '@mui/material/ListItem';
+import ListItemText from '@mui/material/ListItemText';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogContentText from '@mui/material/DialogContentText';
+import DialogActions from '@mui/material/DialogActions';
+import TextField from '@mui/material/TextField';
+import Button from '@mui/material/Button';
+
+export default function EditTextPreference(props: EditTextPreferenceProps) {
+ const {
+ title, summary, dialogTitle, dialogMessage, currentValue, updateValue,
+ } = props;
+
+ const [internalCurrentValue, setInternalCurrentValue] = useState(currentValue);
+ const [dialogOpen, setDialogOpen] = useState(false);
+
+ const handleDialogCancel = () => {
+ setDialogOpen(false);
+
+ // reset the dialog
+ setInternalCurrentValue(currentValue);
+ };
+
+ const handleDialogSubmit = () => {
+ setDialogOpen(false);
+
+ updateValue(internalCurrentValue);
+ };
+
+ return (
+ <>
+ setDialogOpen(true)}
+ >
+
+
+
+ >
+ );
+}
diff --git a/src/components/manga/sourceConfiguration/ListPreference.tsx b/src/components/manga/sourceConfiguration/ListPreference.tsx
new file mode 100644
index 00000000..839e5a15
--- /dev/null
+++ b/src/components/manga/sourceConfiguration/ListPreference.tsx
@@ -0,0 +1,149 @@
+/*
+ * 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, { useState, useEffect } from 'react';
+import ListItem from '@mui/material/ListItem';
+import ListItemText from '@mui/material/ListItemText';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import RadioGroup from '@mui/material/RadioGroup';
+import Radio from '@mui/material/Radio';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import Button from '@mui/material/Button';
+
+interface IListDialogProps{
+ value: string
+ open: boolean
+ onClose: (arg0: string | null) => void
+ options: string[]
+}
+
+function ListDialog(props: IListDialogProps) {
+ const {
+ value: valueProp, open, onClose, options,
+ } = props;
+ const [value, setValue] = React.useState(valueProp);
+ const radioGroupRef = React.useRef(null);
+
+ React.useEffect(() => {
+ if (!open) {
+ setValue(valueProp);
+ }
+ }, [valueProp, open]);
+
+ const handleEntering = () => {
+ if (radioGroupRef.current != null) {
+ radioGroupRef?.current.focus();
+ }
+ };
+
+ const handleCancel = () => {
+ onClose(null);
+ };
+
+ const handleOk = () => {
+ onClose(value);
+ };
+
+ const handleChange = (event: any) => {
+ setValue(event.target.value);
+ };
+
+ return (
+
+ );
+}
+
+export default function ListPreference(props: ListPreferenceProps) {
+ const {
+ title, summary, currentValue, updateValue, entryValues, entries,
+ } = props;
+ const [internalCurrentValue, setInternalCurrentValue] = useState(currentValue);
+ const [dialogOpen, setDialogOpen] = useState(false);
+
+ useEffect(() => {
+ setInternalCurrentValue(currentValue);
+ }, [currentValue]);
+
+ const findEntryOf = (value: string) => {
+ const idx = entryValues.indexOf(value);
+ return entries[idx];
+ };
+
+ const findEntryValueOf = (value: string) => {
+ const idx = entries.indexOf(value);
+ return entryValues[idx];
+ };
+
+ const getSummary = () => {
+ if (summary === '%s') {
+ return findEntryOf(currentValue);
+ }
+ return summary;
+ };
+
+ const handleDialogClose = (newValue: string | null) => {
+ if (newValue !== null) {
+ updateValue(findEntryValueOf(newValue));
+
+ // appear smooth
+ setInternalCurrentValue(newValue);
+ }
+
+ setDialogOpen(false);
+ };
+
+ return (
+ <>
+ setDialogOpen(true)}
+ >
+
+
+
+ >
+ );
+}
diff --git a/src/components/manga/sourceConfiguration/TwoStatePreference.tsx b/src/components/manga/sourceConfiguration/TwoStatePreference.tsx
new file mode 100644
index 00000000..674ad781
--- /dev/null
+++ b/src/components/manga/sourceConfiguration/TwoStatePreference.tsx
@@ -0,0 +1,59 @@
+/*
+ * 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, { useState, useEffect } from 'react';
+import ListItem from '@mui/material/ListItem';
+import ListItemText from '@mui/material/ListItemText';
+import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
+import Switch from '@mui/material/Switch';
+import Checkbox from '@mui/material/Checkbox';
+
+function getTwoStateType(type: 'Checkbox' | 'Switch') {
+ if (type === 'Switch') { return Switch; }
+ return Checkbox;
+}
+
+function TwoSatePreference(props: TwoStatePreferenceProps) {
+ const {
+ title, summary, currentValue, updateValue, type,
+ } = props;
+ const [internalCurrentValue, setInternalCurrentValue] = useState(currentValue);
+
+ useEffect(() => {
+ setInternalCurrentValue(currentValue);
+ }, [currentValue]);
+
+ return (
+
+
+
+ {React.createElement(getTwoStateType(type),
+ {
+ edge: 'end',
+ checked: internalCurrentValue,
+ onChange: () => {
+ updateValue(!currentValue);
+
+ // appear smooth
+ setInternalCurrentValue(!currentValue);
+ },
+ })}
+
+
+ );
+}
+
+export function CheckBoxPreference(props: CheckBoxPreferenceProps) {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+}
+export function SwitchPreferenceCompat(props: SwitchPreferenceCompatProps) {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+}
+
+export default { CheckBoxPreference, SwitchPreferenceCompat };
diff --git a/src/screens/manga/SourceConfigure.tsx b/src/screens/manga/SourceConfigure.tsx
index 7f72d7e0..90828a8e 100644
--- a/src/screens/manga/SourceConfigure.tsx
+++ b/src/screens/manga/SourceConfigure.tsx
@@ -9,13 +9,22 @@ import React, { useContext, useEffect, useState } from 'react';
import NavbarContext from 'context/NavbarContext';
import { useParams } from 'react-router-dom';
import client from 'util/client';
-import CheckBoxPreference from 'components/manga/sourceConfiguration/CheckBoxPreference';
+import { SwitchPreferenceCompat, CheckBoxPreference } from 'components/manga/sourceConfiguration/TwoStatePreference';
+import ListPreference from 'components/manga/sourceConfiguration/ListPreference';
+import EditTextPreference from 'components/manga/sourceConfiguration/EditTextPreference';
import List from '@mui/material/List';
+import cloneObject from 'util/cloneObject';
function getPrefComponent(type: string) {
switch (type) {
case 'CheckBoxPreference':
return CheckBoxPreference;
+ case 'SwitchPreferenceCompat':
+ return SwitchPreferenceCompat;
+ case 'ListPreference':
+ return ListPreference;
+ case 'EditTextPreference':
+ return EditTextPreference;
default:
return CheckBoxPreference;
}
@@ -25,6 +34,9 @@ export default function SourceConfigure() {
const [sourcePreferences, setSourcePreferences] = useState([]);
const { setTitle, setAction } = useContext(NavbarContext);
+ const [updateTriggerHolder, setUpdateTriggerHolder] = useState(0); // just a hack
+ const triggerUpdate = () => setUpdateTriggerHolder(updateTriggerHolder + 1); // just a hack
+
useEffect(() => { setTitle('Source Configuration'); setAction(<>>); }, []);
const { sourceId } = useParams<{ sourceId: string }>();
@@ -33,14 +45,29 @@ export default function SourceConfigure() {
client.get(`/api/v1/source/${sourceId}/preferences`)
.then((response) => response.data)
.then((data) => setSourcePreferences(data));
- }, []);
+ }, [updateTriggerHolder]);
+
+ const updateValue = (position: number) => (
+ (value: any) => {
+ client.post(`/api/v1/source/${sourceId}/preferences`,
+ JSON.stringify({ position, value: value.toString() }))
+ .then(() => triggerUpdate());
+ }
+ );
return (
<>
-
{sourcePreferences.map(
- (it) => React.createElement(getPrefComponent(it.type), it.props),
+ (it, index) => {
+ const props = cloneObject(it.props);
+ props.updateValue = updateValue(index);
+ props.key = index;
+
+ // TypeScript is dumb in detecting extra props
+ // @ts-ignore
+ return React.createElement(getPrefComponent(it.type), props);
+ },
)}
>
diff --git a/src/screens/manga/SourceMangas.tsx b/src/screens/manga/SourceMangas.tsx
index 791f205e..dc878b46 100644
--- a/src/screens/manga/SourceMangas.tsx
+++ b/src/screens/manga/SourceMangas.tsx
@@ -24,7 +24,10 @@ export default function SourceMangas(props: { popular: boolean }) {
const [lastPageNum, setLastPageNum] = useState(1);
useEffect(() => {
- setTitle('Source');
+ setTitle('Source'); // title is deligated to `MangaGrid` but we set it here once
+ }, []);
+
+ useEffect(() => {
setAction(
<>
{isConfigurable && (
@@ -34,8 +37,6 @@ export default function SourceMangas(props: { popular: boolean }) {
edge="end"
color="inherit"
size="large"
- // TODO: enable source configuration
- style={{ display: 'none' }}
>
diff --git a/src/typings.d.ts b/src/typings.d.ts
index fe54e47f..2ad6f657 100644
--- a/src/typings.d.ts
+++ b/src/typings.d.ts
@@ -142,7 +142,7 @@ interface INavbarOverride {
}
type ReaderType =
-'ContinuesVertical'|
+'ContinuesVertical' |
'Webtoon' |
'SingleVertical' |
'SingleRTL' |
@@ -150,7 +150,7 @@ type ReaderType =
'DoubleVertical' |
'DoubleRTL' |
'DoubleLTR' |
-'ContinuesHorizontalLTR'|
+'ContinuesHorizontalLTR' |
'ContinuesHorizontalRTL';
interface IReaderSettings{
@@ -200,11 +200,6 @@ interface IQueue {
queue: IDownloadChapter[]
}
-interface SourcePreferences {
- type: string
- props: any
-}
-
interface PreferenceProps {
key: string
title: string
@@ -212,8 +207,29 @@ interface PreferenceProps {
defaultValue: any
currentValue: any
defaultValueType: string
+
+ // intetnal props
+ updateValue: any
}
-interface CheckBoxPreferenceProps extends PreferenceProps {
+interface TwoStatePreferenceProps extends PreferenceProps {
+ // intetnal props
+ type: 'Switch' | 'Checkbox'
+}
+interface CheckBoxPreferenceProps extends PreferenceProps {}
+interface SwitchPreferenceCompatProps extends PreferenceProps {}
+interface ListPreferenceProps extends PreferenceProps {
+ entries: string[]
+ entryValues: string[]
+}
+interface EditTextPreferenceProps extends PreferenceProps {
+ dialogTitle: string
+ dialogMessage: string
+ text: string
+}
+
+interface SourcePreferences {
+ type: string
+ props: any
}