fix: Export to EPUB Improvements (#670)
* Test config * update react-native-epub-creator * updated deps * option for export to epub * added prettier to dev deps since I couldn't push * removed white background when pulling up bottomsheet * added options to the export to epub feature * support for customCSS -JS and inchapter images * package update * fixed weird typing * fixed weird KeyboardAvoidingView * remove test config * logic fix * removed styles2 * fixed package problems * fixed package problems * notification icons * package reinstallation * package reinstallation * customFileModal fix * fixed ChapterItem alignment * added github suggestions
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 936 B |
Binary file not shown.
|
After Width: | Height: | Size: 558 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Generated
+1978
-15736
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -16,7 +16,7 @@
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cd-z/react-native-epub-creator": "^2.0.0",
|
||||
"@cd-z/react-native-epub-creator": "^2.0.4",
|
||||
"@gorhom/bottom-sheet": "^4.4.5",
|
||||
"@react-native-async-storage/async-storage": "^1.18.1",
|
||||
"@react-native-community/clipboard": "^1.5.1",
|
||||
@@ -91,10 +91,11 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-react-native": "^3.11.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.3.7",
|
||||
"prettier": "^2.8.8",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
BottomSheetModalProps,
|
||||
} from '@gorhom/bottom-sheet';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useTheme } from '@hooks/useTheme';
|
||||
import { overlay } from 'react-native-paper';
|
||||
|
||||
interface BottomSheetProps extends Omit<BottomSheetModalProps, 'ref'> {
|
||||
bottomSheetRef: Ref<BottomSheetModal> | null;
|
||||
@@ -13,6 +15,7 @@ interface BottomSheetProps extends Omit<BottomSheetModalProps, 'ref'> {
|
||||
|
||||
const BottomSheet: React.FC<BottomSheetProps> = props => {
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
const theme = useTheme();
|
||||
|
||||
const renderBackdrop = useCallback(
|
||||
(backdropProps: BottomSheetBackdropProps) => (
|
||||
@@ -31,6 +34,7 @@ const BottomSheet: React.FC<BottomSheetProps> = props => {
|
||||
backdropComponent={renderBackdrop}
|
||||
handleComponent={null}
|
||||
containerStyle={{ paddingBottom: bottom }}
|
||||
backgroundStyle={{ backgroundColor: overlay(2, theme.surface) }}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -25,7 +25,7 @@ const SwitchSetting: React.FC<SwitchProps> = ({
|
||||
paddingHorizontal: 16,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: description ? 16 : 12,
|
||||
paddingVertical: 12,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
@@ -39,7 +39,6 @@ const SwitchSetting: React.FC<SwitchProps> = ({
|
||||
},
|
||||
text: {
|
||||
fontSize: 16,
|
||||
flex: 1,
|
||||
textAlignVertical: 'center',
|
||||
},
|
||||
switch: { marginLeft: 8 },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
|
||||
interface ReturnType {
|
||||
export interface BooleanHookType {
|
||||
value: boolean;
|
||||
setValue: Dispatch<SetStateAction<boolean>>;
|
||||
setTrue: () => void;
|
||||
@@ -8,7 +8,7 @@ interface ReturnType {
|
||||
toggle: () => void;
|
||||
}
|
||||
|
||||
const useBoolean = (defaultValue?: boolean): ReturnType => {
|
||||
const useBoolean = (defaultValue?: boolean): BooleanHookType => {
|
||||
const [value, setValue] = useState(!!defaultValue);
|
||||
|
||||
const setTrue = useCallback(() => setValue(true), []);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { memo, ReactNode, useState } from 'react';
|
||||
import { View, Text, StyleSheet, Pressable } from 'react-native';
|
||||
import { View, StyleSheet, Pressable } from 'react-native';
|
||||
import { Text } from 'react-native-paper';
|
||||
import color from 'color';
|
||||
|
||||
import { Row } from '../../../components/Common';
|
||||
@@ -73,10 +74,10 @@ const ChapterItem: React.FC<ChapterItemProps> = ({
|
||||
onLongPress={() => onSelectLongPress?.(chapter)}
|
||||
android_ripple={{ color: theme.rippleColor }}
|
||||
>
|
||||
{left && <View style={styles.flex}>{left}</View>}
|
||||
<Row style={styles.row}>
|
||||
{left}
|
||||
{!!bookmark && <ChapterBookmarkButton theme={theme} />}
|
||||
<View>
|
||||
<View style={styles.textContainer}>
|
||||
{isUpdateCard && (
|
||||
<Text
|
||||
style={[
|
||||
@@ -131,16 +132,18 @@ const ChapterItem: React.FC<ChapterItemProps> = ({
|
||||
</View>
|
||||
</View>
|
||||
</Row>
|
||||
<DownloadButton
|
||||
downloadQueue={downloadQueue}
|
||||
chapter={chapter}
|
||||
theme={theme}
|
||||
deleteChapter={deleteChapter}
|
||||
downloadChapter={downloadChapter}
|
||||
hideDeleteChapterMenu={hideDeleteChapterMenu}
|
||||
showDeleteChapterMenu={showDeleteChapterMenu}
|
||||
deleteChapterMenuVisible={deleteChapterMenuVisible}
|
||||
/>
|
||||
<View style={styles.flex}>
|
||||
<DownloadButton
|
||||
downloadQueue={downloadQueue}
|
||||
chapter={chapter}
|
||||
theme={theme}
|
||||
deleteChapter={deleteChapter}
|
||||
downloadChapter={downloadChapter}
|
||||
hideDeleteChapterMenu={hideDeleteChapterMenu}
|
||||
showDeleteChapterMenu={showDeleteChapterMenu}
|
||||
deleteChapterMenuVisible={deleteChapterMenuVisible}
|
||||
/>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
@@ -155,13 +158,28 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
columnGab: 8,
|
||||
},
|
||||
text: {
|
||||
fontSize: 12,
|
||||
},
|
||||
textRow: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 5,
|
||||
},
|
||||
row: { flex: 1, overflow: 'hidden' },
|
||||
flex: {
|
||||
flexBasis: 'auto',
|
||||
flexShrink: 9,
|
||||
},
|
||||
textContainer: {
|
||||
flex: 1,
|
||||
marginVertical: '10%',
|
||||
height: '80%',
|
||||
justifyContent: 'space-evenly',
|
||||
},
|
||||
row: {
|
||||
flexBasis: 10,
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden',
|
||||
height: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ChapterItem, LibraryNovelInfo } from '@database/types';
|
||||
import { showToast } from '@hooks/showToast';
|
||||
import { getChapterFromDb } from '@database/queries/DownloadQueries';
|
||||
import { useReaderSettings } from '@redux/hooks';
|
||||
import { useSettings } from '@hooks/reduxHooks';
|
||||
|
||||
interface extendedNovelInfo extends LibraryNovelInfo {
|
||||
chapters: ChapterItem[];
|
||||
@@ -28,7 +29,15 @@ const EpubIconButton: React.FC<EpubIconButtonProps> = ({
|
||||
}) => {
|
||||
const chooseEpubLocationModal = useBoolean(false);
|
||||
const readerSettings = useReaderSettings();
|
||||
const epubStyle = `html {
|
||||
const {
|
||||
epubUseAppTheme = false,
|
||||
epubUseCustomCSS = false,
|
||||
epubUseCustomJS = false,
|
||||
} = useSettings();
|
||||
|
||||
const epubStyle = `${
|
||||
epubUseAppTheme
|
||||
? `html {
|
||||
scroll-behavior: smooth;
|
||||
overflow-x: hidden;
|
||||
padding-top: ${StatusBar.currentHeight};
|
||||
@@ -43,6 +52,7 @@ const EpubIconButton: React.FC<EpubIconButtonProps> = ({
|
||||
text-align: ${readerSettings.textAlign};
|
||||
line-height: ${readerSettings.lineHeight};
|
||||
font-family: "${readerSettings.fontFamily}";
|
||||
background-color: "${readerSettings.theme}";
|
||||
}
|
||||
hr {
|
||||
margin-top: 20px;
|
||||
@@ -56,9 +66,25 @@ const EpubIconButton: React.FC<EpubIconButtonProps> = ({
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}`
|
||||
: ''
|
||||
}
|
||||
|
||||
${readerSettings.customCSS}`;
|
||||
${
|
||||
epubUseCustomCSS
|
||||
? readerSettings.customCSS
|
||||
.replace(RegExp(`\#sourceId-${novel.sourceId}\\s*\\{`, 'g'), 'body {')
|
||||
.replace(RegExp(`\#sourceId-${novel.sourceId}[^\.\#A-Z]*`, 'gi'), '')
|
||||
: ''
|
||||
}`;
|
||||
|
||||
const epubJS = `let novelName = "${novel.novelName}";
|
||||
let chapterName = "";
|
||||
let sourceId =${novel.sourceId};
|
||||
let chapterId ="";
|
||||
let novelId =${novel.novelId};
|
||||
let html = document.querySelector("chapter").innerHTML;
|
||||
${readerSettings.customJS}
|
||||
`;
|
||||
|
||||
const createEpub = async (uri: string) => {
|
||||
var epub = new EpubBuilder(
|
||||
@@ -70,7 +96,8 @@ const EpubIconButton: React.FC<EpubIconButtonProps> = ({
|
||||
description: novel.novelSummary,
|
||||
author: novel.author,
|
||||
bookId: novel.novelId.toString(),
|
||||
stylesheet: epubStyle,
|
||||
stylesheet: epubStyle || undefined,
|
||||
js: epubUseCustomJS ? epubJS : undefined,
|
||||
},
|
||||
uri,
|
||||
);
|
||||
@@ -85,21 +112,17 @@ const EpubIconButton: React.FC<EpubIconButtonProps> = ({
|
||||
await epub.addChapter({
|
||||
title: downloaded.chapterName?.trim() ?? 'Chapter ' + i,
|
||||
fileName: 'Chapter' + i,
|
||||
htmlBody: downloaded.chapterText,
|
||||
htmlBody: `<chapter data-novel-id='${chapter.novelId}' data-chapter-id='${chapter.chapterId}'>${downloaded.chapterText}</chapter>`,
|
||||
});
|
||||
}
|
||||
}
|
||||
var epubFilePath = await epub.save();
|
||||
showToast('Epub file saved at: ' + epubFilePath);
|
||||
} catch (error) {
|
||||
// remove the temp created folder
|
||||
showToast('Cannot create because: ' + error);
|
||||
console.error(error);
|
||||
|
||||
await epub.discardChanges();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
|
||||
@@ -3,25 +3,42 @@ import { StyleSheet, View } from 'react-native';
|
||||
import { getString } from '@strings/translations';
|
||||
import { Button } from '@components';
|
||||
|
||||
import { Modal, Portal, TextInput, Text } from 'react-native-paper';
|
||||
import { Modal, TextInput, Text } from 'react-native-paper';
|
||||
import { useTheme } from '@hooks/useTheme';
|
||||
import { useSettings } from '@hooks/reduxHooks';
|
||||
import { openDocumentTree } from 'react-native-saf-x';
|
||||
import SwitchSetting from '@components/Switch/Switch';
|
||||
import useBoolean from '@hooks/useBoolean';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setAppSettings } from '@redux/settings/settings.actions';
|
||||
|
||||
interface ChooseEpubLocationModalProps {
|
||||
hideModal: () => void;
|
||||
modalVisible: boolean;
|
||||
onSubmit: (uri: string) => void;
|
||||
onSubmit?: (uri: string) => void;
|
||||
dispatchConfig?: boolean;
|
||||
}
|
||||
|
||||
const ChooseEpubLocationModal: React.FC<ChooseEpubLocationModalProps> = ({
|
||||
hideModal,
|
||||
modalVisible,
|
||||
onSubmit: onSubmitProp,
|
||||
dispatchConfig,
|
||||
}) => {
|
||||
const { epubLocation = '' } = useSettings();
|
||||
const theme = useTheme();
|
||||
const {
|
||||
epubLocation = '',
|
||||
epubUseAppTheme = false,
|
||||
epubUseCustomCSS = false,
|
||||
epubUseCustomJS = false,
|
||||
} = useSettings();
|
||||
const saveConfig = dispatchConfig || epubLocation === '';
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [uri, setUri] = useState(epubLocation);
|
||||
const useAppTheme = useBoolean(epubUseAppTheme);
|
||||
const useCustomCSS = useBoolean(epubUseCustomCSS);
|
||||
const useCustomJS = useBoolean(epubUseCustomJS);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const onDismiss = () => {
|
||||
@@ -30,12 +47,14 @@ const ChooseEpubLocationModal: React.FC<ChooseEpubLocationModalProps> = ({
|
||||
setUri(epubLocation);
|
||||
};
|
||||
|
||||
const onChangeText = (txt: string) => {
|
||||
setUri(txt);
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
onSubmitProp(uri);
|
||||
if (saveConfig) {
|
||||
dispatch(setAppSettings('epubLocation', uri));
|
||||
dispatch(setAppSettings('epubUseAppTheme', useAppTheme.value));
|
||||
dispatch(setAppSettings('epubUseCustomCSS', useCustomCSS.value));
|
||||
dispatch(setAppSettings('epubUseCustomJS', useCustomJS.value));
|
||||
}
|
||||
onSubmitProp?.(uri);
|
||||
hideModal();
|
||||
};
|
||||
|
||||
@@ -51,48 +70,69 @@ const ChooseEpubLocationModal: React.FC<ChooseEpubLocationModalProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={modalVisible}
|
||||
onDismiss={onDismiss}
|
||||
contentContainerStyle={[
|
||||
styles.modalContainer,
|
||||
{ backgroundColor: theme.overlay3 },
|
||||
]}
|
||||
>
|
||||
<View style={styles.modalHeaderCtn}>
|
||||
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
|
||||
{getString('novelScreen.convertToEpubModal.chooseLocation')}
|
||||
</Text>
|
||||
<View style={styles.row}>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
value={uri}
|
||||
placeholder={getString(
|
||||
'novelScreen.convertToEpubModal.pathToFolder',
|
||||
)}
|
||||
onChangeText={onChangeText}
|
||||
onSubmitEditing={onSubmit}
|
||||
mode="outlined"
|
||||
theme={{ colors: { ...theme } }}
|
||||
underlineColor={theme.outline}
|
||||
dense
|
||||
error={error ? true : false}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon="folder-edit-outline"
|
||||
onPress={openFolderPicker}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Modal
|
||||
visible={modalVisible}
|
||||
onDismiss={onDismiss}
|
||||
contentContainerStyle={[
|
||||
styles.modalContainer,
|
||||
{ backgroundColor: theme.overlay3 },
|
||||
]}
|
||||
>
|
||||
<View style={styles.modalHeaderCtn}>
|
||||
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
|
||||
{getString('novelScreen.convertToEpubModal.chooseLocation')}
|
||||
</Text>
|
||||
<View style={styles.row}>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
onChangeText={setUri}
|
||||
value={uri}
|
||||
placeholder={getString(
|
||||
'novelScreen.convertToEpubModal.pathToFolder',
|
||||
)}
|
||||
onSubmitEditing={onSubmit}
|
||||
mode="outlined"
|
||||
theme={{ colors: { ...theme } }}
|
||||
underlineColor={theme.outline}
|
||||
dense
|
||||
error={error ? true : false}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon="folder-edit-outline"
|
||||
onPress={openFolderPicker}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.modalFooterCtn}>
|
||||
<Button title={getString('common.submit')} onPress={onSubmit} />
|
||||
<Button title={getString('common.cancel')} onPress={hideModal} />
|
||||
</View>
|
||||
</Modal>
|
||||
</Portal>
|
||||
</View>
|
||||
<View style={styles.settings}>
|
||||
<SwitchSetting
|
||||
label={getString('novelScreen.convertToEpubModal.useReaderTheme')}
|
||||
value={useAppTheme.value}
|
||||
onPress={useAppTheme.toggle}
|
||||
theme={theme}
|
||||
/>
|
||||
<SwitchSetting
|
||||
label={getString('novelScreen.convertToEpubModal.useCustomCSS')}
|
||||
value={useCustomCSS.value}
|
||||
onPress={useCustomCSS.toggle}
|
||||
theme={theme}
|
||||
/>
|
||||
<SwitchSetting
|
||||
label={getString('novelScreen.convertToEpubModal.useCustomJS')}
|
||||
description={getString(
|
||||
'novelScreen.convertToEpubModal.useCustomJSWarning',
|
||||
)}
|
||||
value={useCustomJS.value}
|
||||
onPress={useCustomJS.toggle}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.modalFooterCtn}>
|
||||
<Button title={getString('common.submit')} onPress={onSubmit} />
|
||||
<Button title={getString('common.cancel')} onPress={hideModal} />
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -103,6 +143,10 @@ const styles = StyleSheet.create({
|
||||
margin: 30,
|
||||
borderRadius: 32,
|
||||
},
|
||||
settings: {
|
||||
marginTop: 12,
|
||||
paddingLeft: 5,
|
||||
},
|
||||
icon: { marginVertical: 0, marginRight: 0 },
|
||||
textInput: { flex: 1 },
|
||||
modalHeaderCtn: {
|
||||
@@ -114,38 +158,15 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row-reverse',
|
||||
paddingHorizontal: 20,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 32,
|
||||
paddingBottom: 20,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 24,
|
||||
marginBottom: 16,
|
||||
},
|
||||
errorText: {
|
||||
paddingTop: 12,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
marginVertical: {
|
||||
marginVertical: 8,
|
||||
},
|
||||
flashlist: {
|
||||
marginTop: 8,
|
||||
height: 300,
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
listContentCtn: {
|
||||
paddingVertical: 8,
|
||||
},
|
||||
dateCtn: {
|
||||
fontSize: 12,
|
||||
marginTop: 2,
|
||||
},
|
||||
listElementContainer: {
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 12,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -316,7 +316,7 @@ const WebViewReader: React.FC<WebViewReaderProps> = props => {
|
||||
let sourceId =${chapterInfo.sourceId};
|
||||
let chapterId =${chapterInfo.chapterId};
|
||||
let novelId =${chapterInfo.novelId};
|
||||
let html = document.getElementsByTagName("chapter")[0]. innerHTML;
|
||||
let html = document.querySelector("chapter").innerHTML;
|
||||
${readerSettings.customJS}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", fn);
|
||||
|
||||
@@ -345,9 +345,7 @@ const GenralSettings: React.FC<GenralSettingsProps> = ({ navigation }) => {
|
||||
<ChooseEpubLocationModal
|
||||
hideModal={convertToEpubModal.setFalse}
|
||||
modalVisible={convertToEpubModal.value}
|
||||
onSubmit={(uri: string) =>
|
||||
dispatch(setAppSettings('epubLocation', uri))
|
||||
}
|
||||
dispatchConfig
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { KeyboardAvoidingView, StyleSheet, Text, View } from 'react-native';
|
||||
import { Portal, Modal, overlay, TextInput } from 'react-native-paper';
|
||||
import { Modal, overlay, TextInput } from 'react-native-paper';
|
||||
import { Button } from '@components/index';
|
||||
import { MD3ThemeType } from '@theme/types';
|
||||
import { getString } from '@strings/translations';
|
||||
import { useAppDispatch } from '@redux/hooks';
|
||||
import { setReaderSettings } from '@redux/settings/settings.actions';
|
||||
import * as DocumentPicker from 'expo-document-picker';
|
||||
import { StorageAccessFramework } from 'expo-file-system';
|
||||
import { showToast } from '@hooks/showToast';
|
||||
import { setReaderSettings } from '@redux/settings/settings.actions';
|
||||
|
||||
interface CustomCSSModalProps {
|
||||
visible: boolean;
|
||||
@@ -35,6 +35,7 @@ const CustomFileModal: React.FC<CustomCSSModalProps> = ({
|
||||
),
|
||||
placeholder = '',
|
||||
}) => {
|
||||
const [text, setText] = useState(customFile);
|
||||
const dispatch = useAppDispatch();
|
||||
let mimeType: string;
|
||||
if (type === 'CSS') {
|
||||
@@ -51,66 +52,68 @@ const CustomFileModal: React.FC<CustomCSSModalProps> = ({
|
||||
});
|
||||
if (rawCSS.type === 'success') {
|
||||
let css = await StorageAccessFramework.readAsStringAsync(rawCSS.uri);
|
||||
setCustomFile(css);
|
||||
setText(css);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
showToast(error.message);
|
||||
}
|
||||
};
|
||||
const dismiss = () => {
|
||||
setCustomFile(text.trim());
|
||||
onDismiss();
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={visible}
|
||||
onDismiss={onDismiss}
|
||||
contentContainerStyle={[
|
||||
styles.modalContainer,
|
||||
{ backgroundColor: overlay(2, theme.surface) },
|
||||
]}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="padding" keyboardVerticalOffset={300}>
|
||||
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={[{ color: theme.onSurfaceVariant }]}>
|
||||
{type === 'CSS'
|
||||
? getString('moreScreen.settingsScreen.readerSettings.cssHint')
|
||||
: getString('moreScreen.settingsScreen.readerSettings.jsHint')}
|
||||
</Text>
|
||||
<TextInput
|
||||
theme={{ colors: { ...theme } }}
|
||||
underlineColor={theme.outline}
|
||||
defaultValue={customFile}
|
||||
onChangeText={setCustomFile}
|
||||
mode="outlined"
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={theme.onSurfaceVariant}
|
||||
multiline
|
||||
style={[
|
||||
{ color: theme.onSurface },
|
||||
styles.fontSizeL,
|
||||
styles.customCSSContainer,
|
||||
]}
|
||||
<Modal
|
||||
visible={visible}
|
||||
onDismiss={dismiss}
|
||||
contentContainerStyle={[
|
||||
styles.modalContainer,
|
||||
{ backgroundColor: overlay(2, theme.surface) },
|
||||
]}
|
||||
>
|
||||
<KeyboardAvoidingView behavior="padding" keyboardVerticalOffset={180}>
|
||||
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={[{ color: theme.onSurfaceVariant }]}>
|
||||
{type === 'CSS'
|
||||
? getString('moreScreen.settingsScreen.readerSettings.cssHint')
|
||||
: getString('moreScreen.settingsScreen.readerSettings.jsHint')}
|
||||
</Text>
|
||||
<TextInput
|
||||
theme={{ colors: { ...theme } }}
|
||||
underlineColor={theme.outline}
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
mode="outlined"
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={theme.onSurfaceVariant}
|
||||
multiline
|
||||
style={[
|
||||
{ color: theme.onSurface },
|
||||
styles.fontSizeL,
|
||||
styles.customCSSContainer,
|
||||
]}
|
||||
/>
|
||||
<View style={styles.customCSSButtons}>
|
||||
<Button
|
||||
onPress={() => {
|
||||
dispatch(setReaderSettings(`custom${type}`, text.trim()));
|
||||
dismiss();
|
||||
}}
|
||||
style={styles.button}
|
||||
title={getString('common.save')}
|
||||
mode="contained"
|
||||
/>
|
||||
<View style={styles.customCSSButtons}>
|
||||
<Button
|
||||
onPress={() => {
|
||||
dispatch(setReaderSettings(`custom${type}`, customFile.trim()));
|
||||
onDismiss();
|
||||
}}
|
||||
style={styles.button}
|
||||
title={getString('common.save')}
|
||||
mode="contained"
|
||||
/>
|
||||
<Button
|
||||
style={styles.button}
|
||||
onPress={openCSS}
|
||||
title={openFileButtonLabel}
|
||||
/>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
</Portal>
|
||||
<Button
|
||||
style={styles.button}
|
||||
onPress={openCSS}
|
||||
title={openFileButtonLabel}
|
||||
/>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
|
||||
|
||||
import { Button, List } from '@components/index';
|
||||
|
||||
import { useAppDispatch, readerSettingType } from '@redux/hooks';
|
||||
import { useAppDispatch, useReaderSettings } from '@redux/hooks';
|
||||
import { useTheme } from '@hooks/useTheme';
|
||||
import { getString } from '@strings/translations';
|
||||
import { setReaderSettings } from '@redux/settings/settings.actions';
|
||||
@@ -12,7 +12,7 @@ import CustomFileModal from '../Modals/CustomFileModal';
|
||||
import useBoolean from '@hooks/useBoolean';
|
||||
|
||||
interface CustomJSSettingsProps {
|
||||
readerSettings: readerSettingType;
|
||||
readerSettings: ReturnType<typeof useReaderSettings>;
|
||||
}
|
||||
|
||||
const CustomJSSettings: React.FC<CustomJSSettingsProps> = ({
|
||||
|
||||
@@ -111,10 +111,12 @@ const UpdateNovelCard: React.FC<UpdateCardProps> = ({
|
||||
title={chapterList[0].novelName}
|
||||
titleStyle={{ fontSize: 14, color: theme.onSurface }}
|
||||
left={() => (
|
||||
<NovelCover
|
||||
navigateToNovel={navigateToNovel}
|
||||
uri={chapterList[0].novelCover}
|
||||
/>
|
||||
<View style={styles.listNovelCover}>
|
||||
<NovelCover
|
||||
navigateToNovel={navigateToNovel}
|
||||
uri={chapterList[0].novelCover}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
descriptionStyle={{ fontSize: 12 }}
|
||||
theme={{ colors: theme }}
|
||||
@@ -196,8 +198,11 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 4,
|
||||
backgroundColor: coverPlaceholderColor,
|
||||
},
|
||||
listNovelCover: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
novelCover: {
|
||||
marginRight: 8,
|
||||
marginRight: 16,
|
||||
},
|
||||
imageContainer: {
|
||||
flex: 1,
|
||||
@@ -217,6 +222,6 @@ const styles = StyleSheet.create({
|
||||
margin: 8,
|
||||
},
|
||||
chapterList: {
|
||||
marginLeft: -64,
|
||||
marginLeft: -40,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -131,8 +131,12 @@
|
||||
}
|
||||
},
|
||||
"convertToEpubModal": {
|
||||
"chooseLocation": "Choose EPUB save location",
|
||||
"pathToFolder": "Path to EPUB save folder"
|
||||
"chooseLocation": "Select EPUB storage directory",
|
||||
"pathToFolder": "Path to EPUB save folder",
|
||||
"useReaderTheme": "Use reader theme",
|
||||
"useCustomCSS": "Use customCSS",
|
||||
"useCustomJS": "Use customJS",
|
||||
"useCustomJSWarning": "May not be supported by all EPUB readers"
|
||||
}
|
||||
},
|
||||
"more": "More",
|
||||
|
||||
@@ -105,6 +105,10 @@ export interface StringMap {
|
||||
'novelScreen.jumpToChapterModal.error.validChapterNumber': 'string';
|
||||
'novelScreen.convertToEpubModal.chooseLocation': 'string';
|
||||
'novelScreen.convertToEpubModal.pathToFolder': 'string';
|
||||
'novelScreen.convertToEpubModal.useReaderTheme': 'string';
|
||||
'novelScreen.convertToEpubModal.useCustomCSS': 'string';
|
||||
'novelScreen.convertToEpubModal.useCustomJS': 'string';
|
||||
'novelScreen.convertToEpubModal.useCustomJSWarning': 'string';
|
||||
'more': 'string';
|
||||
'moreScreen.settings': 'string';
|
||||
'moreScreen.settingsScreen.generalSettings': 'string';
|
||||
|
||||
Reference in New Issue
Block a user