improvement: simplify shared chapter states with react context
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { ChapterInfo, NovelInfo } from '@database/types';
|
||||
|
||||
const defaultValue = {} as {
|
||||
novel: NovelInfo;
|
||||
chapter: ChapterInfo;
|
||||
setChapter: (chapter: ChapterInfo) => void;
|
||||
};
|
||||
|
||||
const ChapterContext = createContext<{
|
||||
novel: NovelInfo;
|
||||
chapter: ChapterInfo;
|
||||
setChapter: (chapter: ChapterInfo) => void;
|
||||
}>(defaultValue);
|
||||
|
||||
export function ChapterContextProvider({
|
||||
children,
|
||||
novel,
|
||||
initialChapter,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
novel: NovelInfo;
|
||||
initialChapter: ChapterInfo;
|
||||
}) {
|
||||
const [chapter, setChapter] = useState(initialChapter);
|
||||
return (
|
||||
<ChapterContext.Provider
|
||||
value={{ novel: novel, chapter: chapter, setChapter: c => setChapter(c) }}
|
||||
>
|
||||
{children}
|
||||
</ChapterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useChapterContext = () => {
|
||||
const { novel, chapter, setChapter } = useContext(ChapterContext);
|
||||
return { novel, chapter, setChapter };
|
||||
};
|
||||
@@ -2,11 +2,7 @@ import React, { useRef, useCallback } from 'react';
|
||||
import { DrawerLayoutAndroid } from 'react-native';
|
||||
|
||||
import { showToast } from '@utils/showToast';
|
||||
import {
|
||||
useChapterGeneralSettings,
|
||||
useTheme,
|
||||
useNovel,
|
||||
} from '@hooks/persisted';
|
||||
import { useChapterGeneralSettings, useTheme } from '@hooks/persisted';
|
||||
|
||||
import ReaderAppbar from './components/ReaderAppbar';
|
||||
import ReaderFooter from './components/ReaderFooter';
|
||||
@@ -21,35 +17,28 @@ import WebView from 'react-native-webview';
|
||||
import { getString } from '@strings/translations';
|
||||
import KeepScreenAwake from './components/KeepScreenAwake';
|
||||
import useChapter from './hooks/useChapter';
|
||||
import { ChapterContextProvider, useChapterContext } from './ChapterContext';
|
||||
|
||||
const Chapter = ({ route, navigation }: ChapterScreenProps) => {
|
||||
const drawerRef = useRef<DrawerLayoutAndroid>(null);
|
||||
const { chapters, novelSettings, pages, setPageIndex } = useNovel(
|
||||
route.params.novel.path,
|
||||
route.params.novel.pluginId,
|
||||
);
|
||||
return (
|
||||
<DrawerLayoutAndroid
|
||||
ref={drawerRef}
|
||||
drawerWidth={300}
|
||||
drawerPosition="left"
|
||||
renderNavigationView={() => (
|
||||
<ChapterDrawer
|
||||
<ChapterContextProvider
|
||||
novel={route.params.novel}
|
||||
initialChapter={route.params.chapter}
|
||||
>
|
||||
<DrawerLayoutAndroid
|
||||
ref={drawerRef}
|
||||
drawerWidth={300}
|
||||
drawerPosition="left"
|
||||
renderNavigationView={() => <ChapterDrawer navigation={navigation} />}
|
||||
>
|
||||
<ChapterContent
|
||||
route={route}
|
||||
navigation={navigation}
|
||||
chapters={chapters}
|
||||
novelSettings={novelSettings}
|
||||
pages={pages}
|
||||
setPageIndex={setPageIndex}
|
||||
drawerRef={drawerRef}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<ChapterContent
|
||||
route={route}
|
||||
navigation={navigation}
|
||||
drawerRef={drawerRef}
|
||||
/>
|
||||
</DrawerLayoutAndroid>
|
||||
</DrawerLayoutAndroid>
|
||||
</ChapterContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -58,11 +47,10 @@ type ChapterContentProps = ChapterScreenProps & {
|
||||
};
|
||||
|
||||
export const ChapterContent = ({
|
||||
route,
|
||||
navigation,
|
||||
drawerRef,
|
||||
}: ChapterContentProps) => {
|
||||
const { novel, chapter } = route.params;
|
||||
const { novel, chapter } = useChapterContext();
|
||||
const webViewRef = useRef<WebView>(null);
|
||||
const readerSheetRef = useRef(null);
|
||||
const theme = useTheme();
|
||||
@@ -78,10 +66,9 @@ export const ChapterContent = ({
|
||||
setError,
|
||||
setLoading,
|
||||
getChapter,
|
||||
setChapter,
|
||||
saveProgress,
|
||||
hideHeader,
|
||||
} = useChapter(novel, chapter, webViewRef);
|
||||
} = useChapter(webViewRef);
|
||||
|
||||
const scrollToStart = () =>
|
||||
requestAnimationFrame(() => {
|
||||
@@ -160,7 +147,6 @@ export const ChapterContent = ({
|
||||
<>
|
||||
{keepScreenOn ? <KeepScreenAwake /> : null}
|
||||
<WebViewReader
|
||||
data={{ novel, chapter }}
|
||||
html={chapterText}
|
||||
nextChapter={nextChapter}
|
||||
webViewRef={webViewRef}
|
||||
@@ -172,17 +158,9 @@ export const ChapterContent = ({
|
||||
<ReaderBottomSheetV2 bottomSheetRef={readerSheetRef} />
|
||||
{!hidden ? (
|
||||
<>
|
||||
<ReaderAppbar
|
||||
novelName={novel.name}
|
||||
chapter={chapter}
|
||||
setChapter={setChapter}
|
||||
goBack={navigation.goBack}
|
||||
theme={theme}
|
||||
/>
|
||||
<ReaderAppbar goBack={navigation.goBack} theme={theme} />
|
||||
<ReaderFooter
|
||||
theme={theme}
|
||||
chapter={chapter}
|
||||
novel={novel}
|
||||
nextChapter={nextChapter}
|
||||
prevChapter={prevChapter}
|
||||
readerSheetRef={readerSheetRef}
|
||||
|
||||
@@ -15,15 +15,10 @@ import { getString } from '@strings/translations';
|
||||
import { ChapterScreenProps } from '@navigators/types';
|
||||
import { ChapterInfo } from '@database/types';
|
||||
import { ThemeColors } from '@theme/types';
|
||||
import { NovelSettings } from '@hooks/persisted/useNovel';
|
||||
import { useNovel } from '@hooks/persisted/useNovel';
|
||||
import renderListChapter from './RenderListChapter';
|
||||
import { useChapterContext } from '@screens/reader/ChapterContext';
|
||||
|
||||
type ChapterDrawerProps = ChapterScreenProps & {
|
||||
chapters: ChapterInfo[];
|
||||
novelSettings: NovelSettings;
|
||||
pages: string[];
|
||||
setPageIndex: (value: number) => void;
|
||||
};
|
||||
type ButtonProperties = {
|
||||
text: string;
|
||||
index?: number;
|
||||
@@ -35,13 +30,15 @@ type ButtonsProperties = {
|
||||
};
|
||||
|
||||
const ChapterDrawer = ({
|
||||
route,
|
||||
navigation,
|
||||
chapters,
|
||||
novelSettings,
|
||||
pages,
|
||||
setPageIndex,
|
||||
}: ChapterDrawerProps) => {
|
||||
}: {
|
||||
navigation: ChapterScreenProps['navigation'];
|
||||
}) => {
|
||||
const { novel: novelItem, chapter } = useChapterContext();
|
||||
const { chapters, novelSettings, pages, setPageIndex } = useNovel(
|
||||
novelItem.path,
|
||||
novelItem.pluginId,
|
||||
);
|
||||
const theme = useTheme();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { defaultChapterSort } = useAppSettings();
|
||||
@@ -49,7 +46,6 @@ const ChapterDrawer = ({
|
||||
|
||||
const styles = createStylesheet(theme, insets);
|
||||
|
||||
const { chapter, novel: novelItem } = route.params;
|
||||
const { sort = defaultChapterSort } = novelSettings;
|
||||
const listAscending = sort === 'ORDER BY position ASC';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { StatusBar, StyleSheet, View } from 'react-native';
|
||||
import color from 'color';
|
||||
|
||||
@@ -6,24 +6,20 @@ import { Text } from 'react-native-paper';
|
||||
import { IconButtonV2 } from '../../../components';
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
import { ThemeColors } from '@theme/types';
|
||||
import { ChapterInfo } from '@database/types';
|
||||
import { bookmarkChapter } from '@database/queries/ChapterQueries';
|
||||
import { useChapterContext } from '../ChapterContext';
|
||||
|
||||
interface ReaderAppbarProps {
|
||||
novelName: string;
|
||||
chapter: ChapterInfo;
|
||||
theme: ThemeColors;
|
||||
goBack: () => void;
|
||||
setChapter: (chapter: ChapterInfo) => void;
|
||||
}
|
||||
|
||||
const ReaderAppbar = ({
|
||||
novelName,
|
||||
chapter,
|
||||
goBack,
|
||||
theme,
|
||||
setChapter,
|
||||
}: ReaderAppbarProps) => {
|
||||
const ReaderAppbar = ({ goBack, theme }: ReaderAppbarProps) => {
|
||||
const { chapter, novel } = useChapterContext();
|
||||
const [bookmarked, setBookmarked] = useState(chapter.bookmark);
|
||||
useEffect(() => {
|
||||
setBookmarked(chapter.bookmark);
|
||||
}, [chapter]);
|
||||
return (
|
||||
<Animated.View
|
||||
entering={FadeIn.duration(150)}
|
||||
@@ -49,7 +45,7 @@ const ReaderAppbar = ({
|
||||
style={[styles.title, { color: theme.onSurface }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{novelName}
|
||||
{novel.name}
|
||||
</Text>
|
||||
<Text
|
||||
style={[styles.subtitle, { color: theme.onSurfaceVariant }]}
|
||||
@@ -59,15 +55,10 @@ const ReaderAppbar = ({
|
||||
</Text>
|
||||
</View>
|
||||
<IconButtonV2
|
||||
name={chapter.bookmark ? 'bookmark' : 'bookmark-outline'}
|
||||
name={bookmarked ? 'bookmark' : 'bookmark-outline'}
|
||||
size={24}
|
||||
onPress={() => {
|
||||
bookmarkChapter(chapter.id).then(() =>
|
||||
setChapter({
|
||||
...chapter,
|
||||
bookmark: !chapter.bookmark,
|
||||
}),
|
||||
);
|
||||
bookmarkChapter(chapter.id).then(() => setBookmarked(!bookmarked));
|
||||
}}
|
||||
color={theme.onSurface}
|
||||
theme={theme}
|
||||
|
||||
@@ -4,15 +4,14 @@ import { IconButton } from 'react-native-paper';
|
||||
import color from 'color';
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
import { ThemeColors } from '@theme/types';
|
||||
import { ChapterInfo, NovelInfo } from '@database/types';
|
||||
import { ChapterInfo } from '@database/types';
|
||||
import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
||||
import { ChapterScreenProps } from '@navigators/types';
|
||||
import { useDeviceOrientation } from '@hooks/index';
|
||||
import { useChapterContext } from '../ChapterContext';
|
||||
|
||||
interface ChapterFooterProps {
|
||||
theme: ThemeColors;
|
||||
novel: NovelInfo;
|
||||
chapter: ChapterInfo;
|
||||
nextChapter: ChapterInfo;
|
||||
prevChapter: ChapterInfo;
|
||||
readerSheetRef: React.RefObject<BottomSheetModalMethods>;
|
||||
@@ -24,8 +23,6 @@ interface ChapterFooterProps {
|
||||
|
||||
const ChapterFooter = ({
|
||||
theme,
|
||||
novel,
|
||||
chapter,
|
||||
nextChapter,
|
||||
prevChapter,
|
||||
readerSheetRef,
|
||||
@@ -34,6 +31,7 @@ const ChapterFooter = ({
|
||||
navigation,
|
||||
openDrawer,
|
||||
}: ChapterFooterProps) => {
|
||||
const { novel, chapter } = useChapterContext();
|
||||
const rippleConfig = {
|
||||
color: theme.rippleColor,
|
||||
borderless: true,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
NativeEventEmitter,
|
||||
@@ -28,6 +27,7 @@ import * as Speech from 'expo-speech';
|
||||
import * as Clipboard from 'expo-clipboard';
|
||||
import { showToast } from '@utils/showToast';
|
||||
import { PLUGIN_STORAGE } from '@utils/Storages';
|
||||
import { useChapterContext } from '../ChapterContext';
|
||||
|
||||
type WebViewPostEvent = {
|
||||
type: string;
|
||||
@@ -35,12 +35,6 @@ type WebViewPostEvent = {
|
||||
};
|
||||
|
||||
type WebViewReaderProps = {
|
||||
data: {
|
||||
novel: {
|
||||
pluginId: string;
|
||||
};
|
||||
chapter: ChapterInfo;
|
||||
};
|
||||
html: string;
|
||||
nextChapter: ChapterInfo;
|
||||
webViewRef: React.RefObject<WebView>;
|
||||
@@ -64,7 +58,6 @@ const onLogMessage = (payload: { nativeEvent: { data: string } }) => {
|
||||
|
||||
const WebViewReader: React.FC<WebViewReaderProps> = props => {
|
||||
const {
|
||||
data: { chapter, novel },
|
||||
html,
|
||||
nextChapter,
|
||||
webViewRef,
|
||||
@@ -73,6 +66,7 @@ const WebViewReader: React.FC<WebViewReaderProps> = props => {
|
||||
onPress,
|
||||
navigateToChapterBySwipe,
|
||||
} = props;
|
||||
const { novel, chapter } = useChapterContext();
|
||||
const assetsUriPrefix = useMemo(
|
||||
() => (__DEV__ ? 'http://localhost:8081/assets' : 'file:///android_asset'),
|
||||
[],
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
updateChapterProgress,
|
||||
} from '@database/queries/ChapterQueries';
|
||||
import { insertHistory } from '@database/queries/HistoryQueries';
|
||||
import { ChapterInfo, NovelInfo } from '@database/types';
|
||||
import { ChapterInfo } from '@database/types';
|
||||
import {
|
||||
useChapterGeneralSettings,
|
||||
useLibrarySettings,
|
||||
@@ -25,17 +25,14 @@ import { Dimensions, NativeEventEmitter } from 'react-native';
|
||||
import VolumeButtonListener from '@native/volumeButtonListener';
|
||||
import * as Speech from 'expo-speech';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { useChapterContext } from '../ChapterContext';
|
||||
|
||||
const emmiter = new NativeEventEmitter(VolumeButtonListener);
|
||||
|
||||
export default function useChapter(
|
||||
novel: NovelInfo,
|
||||
initialChapter: ChapterInfo,
|
||||
webViewRef: RefObject<WebView>,
|
||||
) {
|
||||
export default function useChapter(webViewRef: RefObject<WebView>) {
|
||||
const { novel, chapter } = useChapterContext();
|
||||
const { setLastRead } = useNovel(novel.path, novel.pluginId);
|
||||
const [hidden, setHidden] = useState(true);
|
||||
const [chapter, setChapter] = useState(initialChapter);
|
||||
const [chapterText, setChapterText] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [[nextChapter, prevChapter], setAdjacentChapter] = useState<
|
||||
@@ -186,7 +183,6 @@ export default function useChapter(
|
||||
error,
|
||||
loading,
|
||||
chapterText,
|
||||
setChapter,
|
||||
setHidden,
|
||||
setLoading,
|
||||
setError,
|
||||
|
||||
Reference in New Issue
Block a user