custom js, css modal

This commit is contained in:
Nyagami
2024-01-13 22:02:30 +07:00
parent 1253f44413
commit 769ff36352
9 changed files with 783 additions and 420 deletions
+1
View File
@@ -13,3 +13,4 @@ export { default as ColorPreferenceItem } from './ColorPreferenceItem/ColorPrefe
export { default as LoadingMoreIndicator } from './LoadingMoreIndicator/LoadingMoreIndicator';
export { Checkbox } from './Checkbox/Checkbox';
export { RadioButton } from './RadioButton/RadioButton';
export { default as ConfirmationDialog } from './ConfirmationDialog/ConfirmationDialog';
@@ -0,0 +1,130 @@
import React, { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Modal, overlay, TextInput } from 'react-native-paper';
import { StorageAccessFramework } from 'expo-file-system';
import * as DocumentPicker from 'expo-document-picker';
import { Button } from '@components/index';
import { showToast } from '@utils/showToast';
import { useTheme } from '@hooks/persisted';
import { getString } from '@strings/translations';
interface CustomFileModal {
visible: boolean;
onDismiss: () => void;
defaultValue: string;
onSave: (val: string) => void;
title: string;
mimeType: 'text/css' | 'application/javascript';
openFileLabel: string;
placeholder?: string;
description?: string;
}
const CustomFileModal: React.FC<CustomFileModal> = ({
onDismiss,
visible,
defaultValue,
onSave,
title,
mimeType,
openFileLabel,
placeholder,
description,
}) => {
const theme = useTheme();
const [text, setText] = useState('');
const openDocumentPicker = async () => {
try {
const file = await DocumentPicker.getDocumentAsync({
copyToCacheDirectory: false,
type: mimeType,
});
if (file.type === 'success') {
const content = await StorageAccessFramework.readAsStringAsync(
file.uri,
);
onSave(content.trim());
onDismiss();
}
} catch (error: any) {
showToast(error.message);
}
};
return (
<Modal
visible={visible}
onDismiss={onDismiss}
contentContainerStyle={[
styles.modalContainer,
{ backgroundColor: overlay(2, theme.surface) },
]}
>
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
{title}
</Text>
<Text style={[{ color: theme.onSurfaceVariant }]}>{description}</Text>
<TextInput
multiline
mode="outlined"
defaultValue={defaultValue}
onChangeText={setText}
placeholder={placeholder}
placeholderTextColor={theme.onSurfaceVariant}
underlineColor={theme.outline}
style={[{ color: theme.onSurface }, styles.textInput]}
theme={{ colors: { ...theme } }}
/>
<View style={styles.customCSSButtons}>
<Button
onPress={() => {
onSave(text.trim());
onDismiss();
}}
style={styles.button}
title={getString('common.save')}
mode="contained"
/>
<Button
style={styles.button}
onPress={openDocumentPicker}
title={openFileLabel}
/>
</View>
</Modal>
);
};
export default CustomFileModal;
const styles = StyleSheet.create({
modalContainer: {
margin: 30,
padding: 24,
borderRadius: 28,
},
textInput: {
height: 220,
borderRadius: 14,
marginTop: 16,
marginBottom: 8,
fontSize: 16,
},
modalTitle: {
fontSize: 24,
marginBottom: 16,
},
customCSSButtons: {
flexDirection: 'row-reverse',
},
button: {
marginTop: 16,
flex: 1,
marginHorizontal: 8,
},
});
@@ -0,0 +1,110 @@
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { Portal } from 'react-native-paper';
import { Button, List, ConfirmationDialog } from '@components';
import { useBoolean } from '@hooks';
import { useTheme, useChapterReaderSettings } from '@hooks/persisted';
import { getString } from '@strings/translations';
import CustomFileModal from '../Modals/CustomFileModal';
const CustomCSSSettings = () => {
const theme = useTheme();
const { customCSS, setChapterReaderSettings } = useChapterReaderSettings();
const cssModal = useBoolean();
const clearCSSModal = useBoolean();
return (
<>
<View style={styles.header}>
<List.SubHeader theme={theme}>
{getString('moreScreen.settingsScreen.readerSettings.customCSS')}
</List.SubHeader>
</View>
<View style={styles.customCSSContainer}>
<Text numberOfLines={3} style={[{ color: theme.onSurface }]}>
{customCSS || 'Example: body {margin: 10px;}'}
</Text>
<View style={styles.customCSSButtons}>
<Button
onPress={cssModal.setTrue}
style={styles.marginLeftS}
title={getString('common.edit')}
/>
<Button
onPress={clearCSSModal.setTrue}
title={getString('common.clear')}
/>
</View>
</View>
{/* Modals */}
<Portal>
<CustomFileModal
visible={cssModal.value}
onDismiss={cssModal.setFalse}
defaultValue={customCSS}
mimeType="text/css"
title={getString(
'moreScreen.settingsScreen.readerSettings.customCSS',
)}
description={getString(
'moreScreen.settingsScreen.readerSettings.cssHint',
)}
placeholder="Example: body {margin: 10px;}"
openFileLabel={getString(
'moreScreen.settingsScreen.readerSettings.openCSSFile',
)}
onSave={text => setChapterReaderSettings({ customCSS: text })}
/>
<ConfirmationDialog
title={getString(
'moreScreen.settingsScreen.readerSettings.clearCustomCSS',
)}
visible={clearCSSModal.value}
onSubmit={() => {
setChapterReaderSettings({ customCSS: '' });
}}
onDismiss={clearCSSModal.setFalse}
theme={theme}
/>
</Portal>
</>
);
};
export default CustomCSSSettings;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
bottomInset: {
paddingBottom: 40,
},
fontSizeL: {
fontSize: 16,
},
customCSSContainer: {
marginHorizontal: 16,
marginBottom: 8,
},
buttons: {
flex: 1,
},
marginLeftS: {
marginLeft: 8,
},
customCSSButtons: {
marginVertical: 8,
flex: 1,
flexDirection: 'row-reverse',
},
customThemeButton: {
marginHorizontal: 16,
marginBottom: 16,
},
header: { flexDirection: 'row', justifyContent: 'space-between' },
});
@@ -0,0 +1,93 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Portal } from 'react-native-paper';
import { Button, List, ConfirmationDialog } from '@components/index';
import { useBoolean } from '@hooks';
import { useTheme, useChapterReaderSettings } from '@hooks/persisted';
import { getString } from '@strings/translations';
import CustomFileModal from '../Modals/CustomFileModal';
const CustomJSSettings = () => {
const theme = useTheme();
const { customJS, setChapterReaderSettings } = useChapterReaderSettings();
const jsModal = useBoolean();
const clearJSModal = useBoolean();
return (
<>
<View style={styles.header}>
<List.SubHeader theme={theme}>
{getString('moreScreen.settingsScreen.readerSettings.customJS')}
</List.SubHeader>
</View>
<View style={styles.customJSContainer}>
<Text numberOfLines={3} style={[{ color: theme.onSurface }]}>
{customJS || 'Example: body {margin: 10px;}'}
</Text>
<View style={styles.customJSButtons}>
<Button
onPress={jsModal.setTrue}
style={styles.marginLeftS}
title={getString('common.edit')}
/>
<Button
onPress={clearJSModal.setTrue}
title={getString('common.clear')}
/>
</View>
</View>
{/* Modals */}
<Portal>
<CustomFileModal
visible={jsModal.value}
onDismiss={jsModal.setFalse}
defaultValue={customJS}
mimeType="application/javascript"
title={getString('moreScreen.settingsScreen.readerSettings.customJS')}
description={getString(
'moreScreen.settingsScreen.readerSettings.jsHint',
)}
placeholder="Example: document.getElementById('example');"
openFileLabel={getString(
'moreScreen.settingsScreen.readerSettings.openJSFile',
)}
onSave={text => setChapterReaderSettings({ customJS: text })}
/>
<ConfirmationDialog
title={getString(
'moreScreen.settingsScreen.readerSettings.clearCustomJS',
)}
visible={clearJSModal.value}
onSubmit={() => {
setChapterReaderSettings({ customJS: '' });
}}
onDismiss={clearJSModal.setFalse}
theme={theme}
/>
</Portal>
</>
);
};
export default CustomJSSettings;
const styles = StyleSheet.create({
fontSizeL: {
fontSize: 16,
},
customJSContainer: {
paddingHorizontal: 16,
paddingBottom: 8,
},
marginLeftS: {
marginLeft: 8,
},
customJSButtons: {
flex: 1,
flexDirection: 'row-reverse',
},
header: { flexDirection: 'row', justifyContent: 'space-between' },
});
@@ -0,0 +1,52 @@
import React from 'react';
import { List, SwitchItem } from '@components/index';
import { useChapterGeneralSettings, useTheme } from '@hooks/persisted';
import { getString } from '@strings/translations';
const DisplaySettings: React.FC = () => {
const theme = useTheme();
const {
fullScreenMode = true,
showScrollPercentage = true,
showBatteryAndTime = false,
setChapterGeneralSettings,
} = useChapterGeneralSettings();
return (
<>
<List.SubHeader theme={theme}>
{getString('novelScreen.bottomSheet.display')}
</List.SubHeader>
<SwitchItem
label={getString('readerScreen.bottomSheet.fullscreen')}
value={fullScreenMode}
onPress={() =>
setChapterGeneralSettings({ fullScreenMode: !fullScreenMode })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.showProgressPercentage')}
value={showScrollPercentage}
onPress={() =>
setChapterGeneralSettings({
showScrollPercentage: !showScrollPercentage,
})
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.showBatteryAndTime')}
value={showBatteryAndTime}
onPress={() =>
setChapterGeneralSettings({ showBatteryAndTime: !showBatteryAndTime })
}
theme={theme}
/>
</>
);
};
export default DisplaySettings;
@@ -0,0 +1,177 @@
import {
StyleSheet,
Text,
TextInput,
View,
useWindowDimensions,
} from 'react-native';
import React from 'react';
import { defaultTo } from 'lodash-es';
import { Button, List, SwitchItem } from '@components/index';
import { useTheme, useChapterGeneralSettings } from '@hooks/persisted';
import { getString } from '@strings/translations';
const GeneralSettings: React.FC = () => {
const theme = useTheme();
const {
useVolumeButtons = false,
verticalSeekbar = true,
swipeGestures = false,
autoScroll = false,
autoScrollInterval = 10,
autoScrollOffset = null,
setChapterGeneralSettings,
} = useChapterGeneralSettings();
const areAutoScrollSettingsDefault =
autoScrollInterval === 10 && autoScrollOffset === null;
const { height: screenHeight } = useWindowDimensions();
const labelStyle = [styles.fontSizeL, { color: theme.onSurface }];
return (
<>
<List.SubHeader theme={theme}>
{getString('moreScreen.settingsScreen.generalSettings')}
</List.SubHeader>
<SwitchItem
label={getString('readerScreen.bottomSheet.verticalSeekbar')}
description={getString(
'moreScreen.settingsScreen.readerSettings.verticalSeekbarDesc',
)}
value={verticalSeekbar}
onPress={() =>
setChapterGeneralSettings({ verticalSeekbar: !verticalSeekbar })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.volumeButtonsScroll')}
value={useVolumeButtons}
onPress={() =>
setChapterGeneralSettings({ useVolumeButtons: !useVolumeButtons })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.swipeGestures')}
value={swipeGestures}
onPress={() =>
setChapterGeneralSettings({ swipeGestures: !swipeGestures })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.autoscroll')}
value={autoScroll}
onPress={() => setChapterGeneralSettings({ autoScroll: !autoScroll })}
theme={theme}
/>
{autoScroll ? (
<>
<List.Divider theme={theme} />
<List.SubHeader theme={theme}>
{getString('readerScreen.bottomSheet.autoscroll')}
</List.SubHeader>
<View style={styles.autoScrollInterval}>
<Text style={[labelStyle, styles.paddingRightM]} numberOfLines={2}>
{getString(
'moreScreen.settingsScreen.readerSettings.autoScrollInterval',
)}
</Text>
<TextInput
style={labelStyle}
defaultValue={defaultTo(autoScrollInterval, 10).toString()}
keyboardType="numeric"
onChangeText={text => {
if (text) {
setChapterGeneralSettings({
autoScrollInterval: Number(text),
});
}
}}
/>
</View>
<View style={styles.autoScrollInterval}>
<Text style={[labelStyle, styles.paddingRightM]} numberOfLines={2}>
{getString(
'moreScreen.settingsScreen.readerSettings.autoScrollOffset',
)}
</Text>
<TextInput
style={labelStyle}
defaultValue={defaultTo(
autoScrollOffset,
Math.round(screenHeight),
).toString()}
keyboardType="numeric"
onChangeText={text => {
if (text) {
setChapterGeneralSettings({ autoScrollOffset: Number(text) });
}
}}
/>
</View>
{!areAutoScrollSettingsDefault ? (
<View style={styles.customCSSButtons}>
<Button
style={styles.customThemeButton}
title={getString('common.reset')}
onPress={() => {
setChapterGeneralSettings({ autoScrollInterval: 10 });
setChapterGeneralSettings({ autoScrollOffset: null });
}}
/>
</View>
) : null}
</>
) : null}
</>
);
};
export default GeneralSettings;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
bottomInset: {
paddingBottom: 40,
},
fontSizeL: {
fontSize: 16,
},
autoScrollInterval: {
paddingHorizontal: 16,
paddingBottom: 8,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
customThemeButton: {
marginHorizontal: 16,
marginBottom: 16,
},
customCSSContainer: {
paddingHorizontal: 16,
paddingBottom: 8,
},
marginLeftS: {
marginLeft: 8,
},
customCSSButtons: {
flex: 1,
flexDirection: 'row-reverse',
},
paddingRightM: {
flex: 1,
paddingRight: 16,
},
label: {
fontSize: 16,
},
});
@@ -0,0 +1,174 @@
import { StyleSheet, View } from 'react-native';
import React from 'react';
import { Button, ColorPreferenceItem, List } from '@components/index';
import { useChapterReaderSettings, useTheme } from '@hooks/persisted';
import { getString } from '@strings/translations';
import ReaderTextAlignSelector from '@screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector';
import ReaderTextSize from '../ReaderTextSize';
import ReaderLineHeight from '@screens/reader/components/ReaderBottomSheet/ReaderLineHeight';
import ReaderThemeSelector from '@screens/reader/components/ReaderBottomSheet/ReaderThemeSelector';
import {
presetReaderThemes,
readerFonts,
} from '@utils/constants/readerConstants';
import { useBoolean } from '@hooks';
import { Portal } from 'react-native-paper';
import ColorPickerModal from '@components/ColorPickerModal/ColorPickerModal';
import FontPickerModal from '../Modals/FontPickerModal';
const ReaderThemeSettings = () => {
const theme = useTheme();
const readerSettings = useChapterReaderSettings();
const labelStyle = [styles.fontSizeL, { color: theme.onSurface }];
const readerFontPickerModal = useBoolean();
const readerBackgroundModal = useBoolean();
const readerTextColorModal = useBoolean();
const isCurrentThemeCustom = readerSettings.customThemes.some(
item =>
item.backgroundColor === readerSettings.theme &&
item.textColor === readerSettings.textColor,
);
const isCurrentThemePreset = presetReaderThemes.some(
item =>
item.backgroundColor === readerSettings.theme &&
item.textColor === readerSettings.textColor,
);
const currentFontName = readerFonts.find(
item => item.fontFamily === readerSettings.fontFamily,
)?.name;
return (
<>
<List.SubHeader theme={theme}>
{getString('moreScreen.settingsScreen.readerSettings.readerTheme')}
</List.SubHeader>
<ReaderThemeSelector
label={getString('moreScreen.settingsScreen.readerSettings.preset')}
labelStyle={labelStyle}
/>
<ColorPreferenceItem
label={getString(
'moreScreen.settingsScreen.readerSettings.backgroundColor',
)}
description={readerSettings.theme}
onPress={readerBackgroundModal.setTrue}
theme={theme}
/>
<ColorPreferenceItem
label={getString('moreScreen.settingsScreen.readerSettings.textColor')}
description={readerSettings.textColor}
onPress={readerTextColorModal.setTrue}
theme={theme}
/>
{isCurrentThemeCustom ? (
<View style={styles.customCSSButtons}>
<Button
style={styles.customThemeButton}
title={getString(
'moreScreen.settingsScreen.readerSettings.deleteCustomTheme',
)}
onPress={() =>
readerSettings.deleteCustomReaderTheme({
backgroundColor: readerSettings.theme,
textColor: readerSettings.textColor,
})
}
/>
</View>
) : !isCurrentThemePreset ? (
<View style={styles.customCSSButtons}>
<Button
style={styles.customThemeButton}
title={getString(
'moreScreen.settingsScreen.readerSettings.saveCustomTheme',
)}
onPress={() =>
readerSettings.saveCustomReaderTheme({
backgroundColor: readerSettings.theme,
textColor: readerSettings.textColor,
})
}
/>
</View>
) : null}
<ReaderTextAlignSelector labelStyle={labelStyle} />
<ReaderTextSize labelStyle={labelStyle} />
<ReaderLineHeight labelStyle={labelStyle} />
<List.Item
title={getString('readerScreen.bottomSheet.fontStyle')}
description={currentFontName}
onPress={readerFontPickerModal.setTrue}
theme={theme}
/>
{/*
Modals
*/}
<Portal>
<ColorPickerModal
title={getString(
'moreScreen.settingsScreen.readerSettings.backgroundColor',
)}
visible={readerBackgroundModal.value}
color={readerSettings.theme}
closeModal={readerBackgroundModal.setFalse}
theme={theme}
onSubmit={color =>
readerSettings.setChapterReaderSettings({ theme: color })
}
/>
<ColorPickerModal
title={getString(
'moreScreen.settingsScreen.readerSettings.textColor',
)}
visible={readerTextColorModal.value}
color={readerSettings.textColor}
closeModal={readerTextColorModal.setFalse}
theme={theme}
onSubmit={color =>
readerSettings.setChapterReaderSettings({ textColor: color })
}
/>
<FontPickerModal
currentFont={readerSettings.fontFamily}
visible={readerFontPickerModal.value}
onDismiss={readerFontPickerModal.setFalse}
/>
</Portal>
</>
);
};
export default ReaderThemeSettings;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
bottomInset: {
paddingBottom: 40,
},
fontSizeL: {
fontSize: 16,
},
customCSSContainer: {
marginHorizontal: 16,
marginBottom: 8,
},
buttons: {
flex: 1,
},
customCSSButtons: {
marginVertical: 8,
flex: 1,
flexDirection: 'row-reverse',
},
customThemeButton: {
marginHorizontal: 16,
marginBottom: 16,
},
});
@@ -1,47 +1,21 @@
import {
StyleSheet,
Text,
TextInput,
View,
ScrollView,
useWindowDimensions,
} from 'react-native';
import React, { useState } from 'react';
import { View, ScrollView } from 'react-native';
import React from 'react';
import { useNavigation } from '@react-navigation/native';
import { defaultTo } from 'lodash-es';
import WebView from 'react-native-webview';
import { Portal } from 'react-native-paper';
import { dummyHTML } from './utils';
import {
Appbar,
Button,
ColorPreferenceItem,
List,
SwitchItem,
} from '@components/index';
import ReaderThemeSelector from '@screens/reader/components/ReaderBottomSheet/ReaderThemeSelector';
import ReaderTextAlignSelector from '@screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector';
import ColorPickerModal from '@components/ColorPickerModal/ColorPickerModal';
import FontPickerModal from './FontPickerModal';
import ReaderLineHeight from '@screens/reader/components/ReaderBottomSheet/ReaderLineHeight';
import ReaderTextSize from './ReaderTextSize';
import { Appbar, List } from '@components/index';
import {
useChapterGeneralSettings,
useChapterReaderSettings,
useTheme,
} from '@hooks/persisted';
import { useChapterReaderSettings, useTheme } from '@hooks/persisted';
import { getString } from '@strings/translations';
import { dummyHTML } from './utils';
import { useBoolean } from '@hooks';
import {
presetReaderThemes,
readerFonts,
} from '@utils/constants/readerConstants';
const READER_HEIGHT = 360;
import GeneralSettings from './Settings/GeneralSettings';
import CustomCSSSettings from './Settings/CustomCSSSettings';
import CustomJSSettings from './Settings/CustomJSSettings';
import DisplaySettings from './Settings/DisplaySettings';
import ReaderThemeSettings from './Settings/ReaderThemeSettings';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export type TextAlignments =
| 'left'
@@ -54,21 +28,17 @@ export type TextAlignments =
const SettingsReaderScreen = () => {
const theme = useTheme();
const navigation = useNavigation();
const { bottom } = useSafeAreaInsets();
const readerSettings = useChapterReaderSettings();
const {
useVolumeButtons,
verticalSeekbar,
swipeGestures,
autoScroll,
autoScrollInterval,
autoScrollOffset,
fullScreenMode,
showScrollPercentage,
showBatteryAndTime,
setChapterGeneralSettings,
} = useChapterGeneralSettings();
const READER_HEIGHT = 280;
const dummyChapterInfo = {
sourceId: 11,
chapterId: 99,
novelId: 20,
novelName: 'novel name',
chapterName: "chapter' name",
};
const webViewCSS = `
<style>
body {
@@ -94,36 +64,6 @@ const SettingsReaderScreen = () => {
const readerBackgroundColor = readerSettings.theme;
const readerBackgroundModal = useBoolean();
const readerTextColorModal = useBoolean();
const readerFontPickerModal = useBoolean();
const isCurrentThemeCustom = readerSettings.customThemes.some(
item =>
item.backgroundColor === readerSettings.theme &&
item.textColor === readerSettings.textColor,
);
const isCurrentThemePreset = presetReaderThemes.some(
item =>
item.backgroundColor === readerSettings.theme &&
item.textColor === readerSettings.textColor,
);
const currentFontName = readerFonts.find(
item => item.fontFamily === readerSettings.fontFamily,
)?.name;
const areAutoScrollSettingsDefault =
autoScrollInterval === 10 && autoScrollOffset === null;
const [customCSS, setcustomCSS] = useState(readerSettings.customCSS);
const [customJS, setcustomJS] = useState(readerSettings.customJS);
const { height: screenHeight } = useWindowDimensions();
const labelStyle = [styles.fontSizeL, { color: theme.onSurface }];
return (
<>
<Appbar
@@ -146,13 +86,24 @@ const SettingsReaderScreen = () => {
${webViewCSS}
</head>
<body>
<chapter>
<chapter
data-novel-id='${dummyChapterInfo.novelId}'
data-chapter-id='${dummyChapterInfo.chapterId}'
>
${dummyHTML}
<script>
async function fn(){${readerSettings.customJS}}
document.addEventListener("DOMContentLoaded", fn);
</script>
</chapter>
<script>
async function fn(){
let novelName = "${dummyChapterInfo.novelName}";
let chapterName = "${dummyChapterInfo.chapterName}";
let sourceId =${dummyChapterInfo.sourceId};
let chapterId =${dummyChapterInfo.chapterId};
let novelId =${dummyChapterInfo.novelId};
let html = document.getElementsByTagName("chapter")[0].innerHTML;
${readerSettings.customJS}
}
document.addEventListener("DOMContentLoaded", fn);
</script>
</body>
</html>
`,
@@ -160,346 +111,21 @@ const SettingsReaderScreen = () => {
/>
</View>
<ScrollView
style={styles.flex}
contentContainerStyle={styles.bottomInset}
>
<SwitchItem
label={getString('readerScreen.bottomSheet.verticalSeekbar')}
description={getString(
'moreScreen.settingsScreen.readerSettings.verticalSeekbarDesc',
)}
value={verticalSeekbar}
onPress={() =>
setChapterGeneralSettings({ verticalSeekbar: !verticalSeekbar })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.volumeButtonsScroll')}
value={useVolumeButtons}
onPress={() =>
setChapterGeneralSettings({ useVolumeButtons: !useVolumeButtons })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.swipeGestures')}
value={swipeGestures}
onPress={() =>
setChapterGeneralSettings({ swipeGestures: !swipeGestures })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.autoscroll')}
value={autoScroll}
onPress={() => setChapterGeneralSettings({ autoScroll: !autoScroll })}
theme={theme}
/>
{autoScroll ? (
<>
<List.Divider theme={theme} />
<List.SubHeader theme={theme}>
{getString('readerScreen.bottomSheet.autoscroll')}
</List.SubHeader>
<View style={styles.autoScrollInterval}>
<Text
style={[labelStyle, styles.paddingRightM]}
numberOfLines={2}
>
{getString(
'moreScreen.settingsScreen.readerSettings.autoScrollInterval',
)}
</Text>
<TextInput
style={labelStyle}
defaultValue={defaultTo(autoScrollInterval, 10).toString()}
keyboardType="numeric"
onChangeText={text => {
if (text) {
setChapterGeneralSettings({
autoScrollInterval: Number(text),
});
}
}}
/>
</View>
<View style={styles.autoScrollInterval}>
<Text
style={[labelStyle, styles.paddingRightM]}
numberOfLines={2}
>
{getString(
'moreScreen.settingsScreen.readerSettings.autoScrollOffset',
)}
</Text>
<TextInput
style={labelStyle}
defaultValue={defaultTo(
autoScrollOffset,
Math.round(screenHeight),
).toString()}
keyboardType="numeric"
onChangeText={text => {
if (text) {
setChapterGeneralSettings({
autoScrollOffset: Number(text),
});
}
}}
/>
</View>
{!areAutoScrollSettingsDefault ? (
<View style={styles.customCSSButtons}>
<Button
style={styles.customThemeButton}
title={getString('common.reset')}
onPress={() => {
setChapterGeneralSettings({
autoScrollInterval: 10,
autoScrollOffset: null,
});
}}
/>
</View>
) : null}
</>
) : null}
<>
<ScrollView>
<View style={{ paddingBottom: bottom }}>
<GeneralSettings />
<List.Divider theme={theme} />
<List.SubHeader theme={theme}>
{getString('moreScreen.settingsScreen.readerSettings.customCSS')}
</List.SubHeader>
<View style={styles.customCSSContainer}>
<TextInput
style={[{ color: theme.onSurface }, styles.fontSizeL]}
value={customCSS}
onChangeText={text => setcustomCSS(text)}
placeholderTextColor={theme.onSurfaceVariant}
placeholder="Example: body { color: red; }"
multiline={true}
/>
<View style={styles.customCSSButtons}>
<Button
onPress={() =>
readerSettings.setChapterReaderSettings({
customCSS: customCSS,
})
}
style={styles.marginLeftS}
title={getString('common.save')}
/>
<Button
onPress={() =>
readerSettings.setChapterReaderSettings({ customCSS: '' })
}
title={getString('common.clear')}
/>
</View>
</View>
<CustomCSSSettings />
<List.Divider theme={theme} />
<List.SubHeader theme={theme}>
{/* {getString('moreScreen.settingsScreen.readerSettings.customJS')} */}
{'Custom JS'}
</List.SubHeader>
<View style={styles.customCSSContainer}>
<TextInput
style={[{ color: theme.onSurface }, styles.fontSizeL]}
value={customJS}
onChangeText={text => setcustomJS(text)}
placeholderTextColor={theme.onSurfaceVariant}
placeholder="Example: document.getElementById('example');"
multiline={true}
/>
<View style={styles.customCSSButtons}>
<Button
onPress={() =>
readerSettings.setChapterReaderSettings({
customJS: customJS,
})
}
style={styles.marginLeftS}
title={getString('common.save')}
/>
<Button
onPress={() =>
readerSettings.setChapterReaderSettings({ customJS: '' })
}
title={getString('common.clear')}
/>
</View>
</View>
</>
<List.Divider theme={theme} />
<List.SubHeader theme={theme}>
{getString('novelScreen.bottomSheet.display')}
</List.SubHeader>
<SwitchItem
label={getString('readerScreen.bottomSheet.fullscreen')}
value={fullScreenMode}
onPress={() =>
setChapterGeneralSettings({ fullScreenMode: !fullScreenMode })
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.showProgressPercentage')}
value={showScrollPercentage}
onPress={() =>
setChapterGeneralSettings({
showScrollPercentage: !showScrollPercentage,
})
}
theme={theme}
/>
<SwitchItem
label={getString('readerScreen.bottomSheet.showBatteryAndTime')}
value={showBatteryAndTime}
onPress={() =>
setChapterGeneralSettings({
showBatteryAndTime: !showBatteryAndTime,
})
}
theme={theme}
/>
<List.Divider theme={theme} />
<List.SubHeader theme={theme}>
{getString('moreScreen.settingsScreen.readerSettings.readerTheme')}
</List.SubHeader>
<ReaderThemeSelector
label={getString('moreScreen.settingsScreen.readerSettings.preset')}
labelStyle={labelStyle}
/>
<ColorPreferenceItem
label={getString(
'moreScreen.settingsScreen.readerSettings.backgroundColor',
)}
description={readerSettings.theme}
onPress={readerBackgroundModal.setTrue}
theme={theme}
/>
<ColorPreferenceItem
label={getString(
'moreScreen.settingsScreen.readerSettings.textColor',
)}
description={readerSettings.textColor}
onPress={readerTextColorModal.setTrue}
theme={theme}
/>
{isCurrentThemeCustom ? (
<View style={styles.customCSSButtons}>
<Button
style={styles.customThemeButton}
title={getString(
'moreScreen.settingsScreen.readerSettings.deleteCustomTheme',
)}
onPress={() =>
readerSettings.deleteCustomReaderTheme({
backgroundColor: readerSettings.theme,
textColor: readerSettings.textColor,
})
}
/>
</View>
) : !isCurrentThemePreset ? (
<View style={styles.customCSSButtons}>
<Button
style={styles.customThemeButton}
title={getString(
'moreScreen.settingsScreen.readerSettings.saveCustomTheme',
)}
onPress={() =>
readerSettings.saveCustomReaderTheme({
backgroundColor: readerSettings.theme,
textColor: readerSettings.textColor,
})
}
/>
</View>
) : null}
<ReaderTextAlignSelector labelStyle={labelStyle} />
<ReaderTextSize labelStyle={labelStyle} />
<ReaderLineHeight labelStyle={labelStyle} />
<List.Item
title={getString('readerScreen.bottomSheet.fontStyle')}
description={currentFontName}
onPress={readerFontPickerModal.setTrue}
theme={theme}
/>
<CustomJSSettings />
<List.Divider theme={theme} />
<DisplaySettings />
<List.Divider theme={theme} />
<ReaderThemeSettings />
</View>
</ScrollView>
<Portal>
<ColorPickerModal
title={getString(
'moreScreen.settingsScreen.readerSettings.backgroundColor',
)}
visible={readerBackgroundModal.value}
color={readerSettings.theme}
closeModal={readerBackgroundModal.setFalse}
theme={theme}
onSubmit={color =>
readerSettings.setChapterReaderSettings({ theme: color })
}
/>
<ColorPickerModal
title={getString(
'moreScreen.settingsScreen.readerSettings.textColor',
)}
visible={readerTextColorModal.value}
color={readerSettings.textColor}
closeModal={readerTextColorModal.setFalse}
theme={theme}
onSubmit={color =>
readerSettings.setChapterReaderSettings({ textColor: color })
}
/>
<FontPickerModal
currentFont={readerSettings.fontFamily}
visible={readerFontPickerModal.value}
onDismiss={readerFontPickerModal.setFalse}
/>
</Portal>
</>
);
};
export default SettingsReaderScreen;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
bottomInset: {
paddingBottom: 40,
},
fontSizeL: {
fontSize: 16,
},
autoScrollInterval: {
paddingHorizontal: 16,
paddingBottom: 8,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
customThemeButton: {
marginHorizontal: 16,
marginBottom: 16,
},
customCSSContainer: {
paddingHorizontal: 16,
paddingBottom: 8,
},
marginLeftS: {
marginLeft: 8,
},
customCSSButtons: {
flex: 1,
flexDirection: 'row-reverse',
},
paddingRightM: {
flex: 1,
paddingRight: 16,
},
});