From f9859d7eca9b93a0498d76b3a2ab5c0992068da2 Mon Sep 17 00:00:00 2001 From: Ashhhleyyy Date: Wed, 2 Aug 2023 17:56:57 +0100 Subject: [PATCH] feat: improve offline support to replace the "local" provider --- App.tsx | 3 + package.json | 6 +- src/components/CachedImage.tsx | 26 + src/components/ChapterList.tsx | 3 +- src/components/Chip.tsx | 2 +- src/components/ComicCard.tsx | 7 +- src/components/ComicSelector.tsx | 4 +- src/pages/chapter.tsx | 3 +- src/pages/comic.tsx | 19 +- src/pages/comiclist/Library.tsx | 2 +- src/pages/comiclist/Local.tsx | 11 +- src/pages/comicprofile.tsx | 12 +- src/pages/debug/cache.tsx | 29 + src/pages/home.tsx | 13 +- src/pages/save.tsx | 33 +- src/provider/cache.ts | 133 ++++ src/provider/comicfury.ts | 2 +- src/provider/download.ts | 49 ++ src/provider/index.ts | 120 ++-- src/provider/local.ts | 13 +- src/provider/provider.ts | 48 ++ src/util.ts | 10 + src/util/fetch.ts | 12 +- yarn.lock | 1044 ++++++++++++++++-------------- 24 files changed, 1006 insertions(+), 598 deletions(-) create mode 100644 src/components/CachedImage.tsx create mode 100644 src/pages/debug/cache.tsx create mode 100644 src/provider/cache.ts create mode 100644 src/provider/download.ts create mode 100644 src/provider/provider.ts create mode 100644 src/util.ts diff --git a/App.tsx b/App.tsx index 95b0b84..abf76b2 100644 --- a/App.tsx +++ b/App.tsx @@ -10,6 +10,7 @@ import { ComicProviderKey } from './src/provider'; import { NonLocalProviderKey } from './src/provider/local'; import Save from './src/pages/save'; import ComicList, { TabParamList } from './src/pages/comiclist'; +import DebugCache from './src/pages/debug/cache'; const Stack = createNativeStackNavigator(); const queryClient = new QueryClient(); @@ -21,6 +22,7 @@ export type RootStackParamList = { Chapter: { provider: ComicProviderKey; comicId: string; chapterId: string; }; Page: { provider: ComicProviderKey; comicId: string; pageId: string; }; Save: { provider: NonLocalProviderKey; comicId: string; }; + DebugCache: undefined; } export default function App() { @@ -36,6 +38,7 @@ export default function App() { + diff --git a/package.json b/package.json index ec24f00..d490754 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@react-navigation/native": "^6.0.12", "@react-navigation/native-stack": "^6.8.0", "cheerio": "^1.0.0-rc.12", + "crypto-js": "^4.1.1", "expo": "^49.0.0", "expo-file-system": "~15.4.2", "expo-status-bar": "~1.6.0", @@ -27,7 +28,7 @@ "react-native-gesture-handler": "~2.12.0", "react-native-image-progress": "^1.2.0", "react-native-modalize": "^2.1.1", - "react-native-progress": "^5.0.0", + "react-native-progress": "https://github.com/EslamElMeniawy/react-native-progress", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", "react-native-svg": "13.9.0", @@ -36,8 +37,9 @@ }, "devDependencies": { "@babel/core": "^7.20.0", + "@types/crypto-js": "^4.1.1", "@types/react": "~18.2.14", - "@types/react-native": "~0.70.6", + "@types/react-native": "^0.72.2", "typescript": "^5.1.3" }, "private": true diff --git a/src/components/CachedImage.tsx b/src/components/CachedImage.tsx new file mode 100644 index 0000000..a3f38b4 --- /dev/null +++ b/src/components/CachedImage.tsx @@ -0,0 +1,26 @@ +import { FC } from "react"; +import { useImageUrl } from "../provider"; +import { ActivityIndicator } from "react-native"; +import { Image as NativeImage, Text } from "react-native"; +import { createImageProgress } from "react-native-image-progress"; +import { Pie } from "react-native-progress"; + +const Image = createImageProgress(NativeImage); + +interface Props { + url: string; + style?: any; + imageStyle?: any; + width?: number; + height?: number; +} + +const CachedImage: FC = (props) => { + const { data, error, isLoading } = useImageUrl(props.url); + if (isLoading) return ; + if (error) return Failed to load image: {error.toString()}; + if (!data) return Something has gone horribly wrong! + return ; +} + +export default CachedImage; diff --git a/src/components/ChapterList.tsx b/src/components/ChapterList.tsx index d9da8b2..9c85a4f 100644 --- a/src/components/ChapterList.tsx +++ b/src/components/ChapterList.tsx @@ -3,7 +3,8 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { FC } from "react"; import { ActivityIndicator, FlatList, StyleSheet, Text, View } from "react-native"; import { RootStackParamList } from "../../App"; -import { SimpleChapter as ChapterData, ComicProviderKey, useComicChapters } from "../provider"; +import { ComicProviderKey, useComicChapters } from "../provider"; +import { SimpleChapter as ChapterData } from "../provider/provider"; import Touchable from "./Touchable"; interface Props { diff --git a/src/components/Chip.tsx b/src/components/Chip.tsx index e97109f..c3d729b 100644 --- a/src/components/Chip.tsx +++ b/src/components/Chip.tsx @@ -1,5 +1,5 @@ import { FC, ReactNode } from "react"; -import { Text, View } from "react-native"; +import { Text } from "react-native"; interface Props { colour: string; diff --git a/src/components/ComicCard.tsx b/src/components/ComicCard.tsx index 6498bf6..7ad3095 100644 --- a/src/components/ComicCard.tsx +++ b/src/components/ComicCard.tsx @@ -1,8 +1,9 @@ import { MaterialCommunityIcons } from "@expo/vector-icons"; import { FC } from "react"; -import { Image, StyleSheet, Text, View } from "react-native"; -import { ComicMetadata } from "../provider"; +import { StyleSheet, Text, View } from "react-native"; +import { ComicMetadata } from "../provider/provider"; import Chip from "./Chip"; +import CachedImage from "./CachedImage"; interface Props { comic: ComicMetadata & { isLocal?: boolean } @@ -10,7 +11,7 @@ interface Props { const ComicCard: FC = (props) => { return - + diff --git a/src/components/ComicSelector.tsx b/src/components/ComicSelector.tsx index 5d5e3e6..6e5dab9 100644 --- a/src/components/ComicSelector.tsx +++ b/src/components/ComicSelector.tsx @@ -1,10 +1,10 @@ import { FC } from "react" import { StyleSheet, View } from "react-native" -import { ComicMetadata } from "../provider" +import { ComicMetadata } from "../provider/provider" import ComicCard from "./ComicCard" import Touchable from "./Touchable" -const ComicSelector: FC void; isLocal?: boolean }> = (props) => { +const ComicSelector: FC void }> = (props) => { return props.onClick()}> diff --git a/src/pages/chapter.tsx b/src/pages/chapter.tsx index 5f21612..83a1dbf 100644 --- a/src/pages/chapter.tsx +++ b/src/pages/chapter.tsx @@ -4,7 +4,8 @@ import { ActivityIndicator, FlatList, Platform, StyleSheet, Text, TouchableNativ import { RootStackParamList } from "../../App"; import Divider from "../components/Divider"; import Touchable from "../components/Touchable"; -import { SimplePage, useComicChapter } from "../provider"; +import { useComicChapter } from "../provider"; +import { SimplePage } from "../provider/provider"; const Page: FC void }> = (props) => { return props.onClick(props.id)}> diff --git a/src/pages/comic.tsx b/src/pages/comic.tsx index be61f8e..fc31751 100644 --- a/src/pages/comic.tsx +++ b/src/pages/comic.tsx @@ -2,15 +2,14 @@ import { MaterialCommunityIcons } from "@expo/vector-icons"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { NativeStackScreenProps } from "@react-navigation/native-stack"; import { useEffect } from "react"; -import { ActivityIndicator, Image as NativeImage, ScrollView, StyleSheet, Text, View } from "react-native"; +import { ActivityIndicator, Dimensions, Image as NativeImage, ScrollView, StyleSheet, Text, View } from "react-native"; import { createImageProgress } from "react-native-image-progress"; import { Pie } from 'react-native-progress'; import { useQueryClient } from "react-query"; import { RootStackParamList } from "../../App"; import Touchable from "../components/Touchable"; import { useComicPage } from "../provider"; - -const Image = createImageProgress(NativeImage); +import CachedImage from "../components/CachedImage"; export default function Comic(props: Props) { const { data, error, isLoading } = useComicPage(props.route.params.provider, props.route.params.comicId, props.route.params.pageId, props.navigation); @@ -24,6 +23,8 @@ export default function Comic(props: Props) { } }, [data]); + const width = Dimensions.get('window').width; + if (isLoading) return ; if (error) return Failed to load comic: {error.toString()}; if (!data) return Whoops! We couldn't find that page :/; @@ -38,10 +39,11 @@ export default function Comic(props: Props) { } return - {data.imageSegments.map((item) => )} + {data.imageSegments.map((item) => )} newPage(data.previousPageId)} disabled={data.previousPageId === undefined}> @@ -61,6 +63,9 @@ export default function Comic(props: Props) { + + {props.route.params.provider}:{props.route.params.comicId}:{props.route.params.pageId} + } diff --git a/src/pages/comiclist/Library.tsx b/src/pages/comiclist/Library.tsx index 36aceae..db4e3e8 100644 --- a/src/pages/comiclist/Library.tsx +++ b/src/pages/comiclist/Library.tsx @@ -55,7 +55,7 @@ export default function Library() { } return - openComic(item.provider, item.comicId)} isLocal={item.provider === 'local'} {...item} /> + openComic(item.provider, item.comicId)} {...item} /> } keyExtractor={item => item.provider + ':' + item.comicId} /> } diff --git a/src/pages/comiclist/Local.tsx b/src/pages/comiclist/Local.tsx index 9c6232e..1ca9b79 100644 --- a/src/pages/comiclist/Local.tsx +++ b/src/pages/comiclist/Local.tsx @@ -2,7 +2,7 @@ import { FC, useEffect } from "react" import { ActivityIndicator, FlatList, StyleSheet, Text, TouchableOpacity, View } from "react-native" import Touchable from "../../components/Touchable"; import ComicCard from '../../components/ComicCard'; -import { ComicMetadata, useLocalComics } from "../../provider" +import { useLocalComics } from "../../provider" import { CompositeNavigationProp, useNavigation } from "@react-navigation/native"; import { BottomTabNavigationProp } from "@react-navigation/bottom-tabs"; import { TabParamList } from "../comiclist"; @@ -54,10 +54,11 @@ export default function LocalComicList() { if (!data) return Whoops! We couldn't those comics :/; function openComic(comicId: LocalComicId) { - navigation.navigate('ComicProfile', { - provider: 'local', - comicId, - }); + alert('oops'); + // navigation.navigate('ComicProfile', { + // provider: 'local', + // comicId, + // }); } if (data.length === 0) { diff --git a/src/pages/comicprofile.tsx b/src/pages/comicprofile.tsx index 20281e3..8ad838c 100644 --- a/src/pages/comicprofile.tsx +++ b/src/pages/comicprofile.tsx @@ -12,15 +12,13 @@ export default function ComicProfile(props: Props) { const comicQuery = useComicMetadata(props.route.params.provider, props.route.params.comicId, (data) => { props.navigation.setOptions({ title: data.title, - headerRight: props.route.params.provider === 'local' ? undefined : () => ( + headerRight: () => ( { const { provider, comicId } = props.route.params; - if (provider !== 'local') { - props.navigation.push('Save', { - provider, - comicId, - }); - } + props.navigation.push('Save', { + provider, + comicId, + }); }}> diff --git a/src/pages/debug/cache.tsx b/src/pages/debug/cache.tsx new file mode 100644 index 0000000..034b00e --- /dev/null +++ b/src/pages/debug/cache.tsx @@ -0,0 +1,29 @@ +import { NativeStackScreenProps } from "@react-navigation/native-stack"; +import { RootStackParamList } from "../../../App"; +import { ScrollView, StyleSheet, Text, View } from "react-native"; +import { CACHES } from "../../provider"; +import { logBuffer } from "../../provider/cache"; + +export default function DebugCache(props: Props) { + return + {CACHES.map((item) => { + return + + {item.name}: {item.size} items + + ; + })} + + LOGS:{'\n'} + {logBuffer.join('\n')} + + +} + +type Props = NativeStackScreenProps; + +const styles = StyleSheet.create({ + item: { + padding: 8, + }, +}); diff --git a/src/pages/home.tsx b/src/pages/home.tsx index afc63b5..70318b4 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import { Button, StyleSheet, Text, TextInput, View } from "react-native"; import { useQueryClient } from "react-query"; import { RootStackParamList } from "../../App"; -import { ComicProviderKey } from "../provider"; +import { CACHES, ComicProviderKey } from "../provider"; import * as FileSystem from 'expo-file-system'; export default function Home(props: Props) { @@ -23,7 +23,7 @@ export default function Home(props: Props) {