improvement: simplify shared chapter states with react context

This commit is contained in:
nyagami
2024-07-16 23:11:41 +07:00
parent 306d8d1d42
commit d5cb0a234f
7 changed files with 88 additions and 97 deletions
+38
View File
@@ -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 };
};
+19 -41
View File
@@ -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';
+11 -20
View File
@@ -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'),
[],
+4 -8
View File
@@ -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,