Spaces:
Running
Running
import React, { useEffect, useState, useCallback, useContext, useRef } from 'react'; | |
import { Link, router, useLocalSearchParams, useNavigation, useFocusEffect } from 'expo-router'; | |
import { Image as RNImage, StyleSheet, useWindowDimensions, ScrollView, Pressable, RefreshControl, Platform } from 'react-native'; | |
import { SafeAreaView } from 'react-native-safe-area-context'; | |
import { Icon, MD3Colors, Button, Text, TextInput, TouchableRipple } from 'react-native-paper'; | |
import CircularProgress from 'react-native-circular-progress-indicator'; | |
import { ActivityIndicator } from 'react-native-paper'; | |
import uuid from 'react-native-uuid'; | |
import Toast from 'react-native-toast-message'; | |
import { View, AnimatePresence } from 'moti'; | |
import * as Clipboard from 'expo-clipboard'; | |
import * as FileSystem from 'expo-file-system'; | |
import NetInfo from "@react-native-community/netinfo"; | |
import JSZip from 'jszip'; | |
import ComicStorage from '@/constants/module/storages/comic_storage'; | |
import ChapterStorage from '@/constants/module/storages/chapter_storage'; | |
import ChapterDataStorage from '@/constants/module/storages/chapter_data_storage'; | |
import Image from '@/components/Image'; | |
import {CONTEXT} from '@/constants/module/context'; | |
import {blobToBase64, base64ToBlob} from "@/constants/module/file_manager"; | |
import Theme from '@/constants/theme'; | |
import { get_chapter } from '../modules/get_chapter'; | |
const ChapterImage = ({item, zoom, showOptions, setShowOptions, setIsLoading, SET_DATA}:any)=>{ | |
const SOURCE = useLocalSearchParams().source; | |
const COMIC_ID = useLocalSearchParams().comic_id; | |
const CHAPTER_IDX = Number(useLocalSearchParams().chapter_idx as string); | |
const Dimensions = useWindowDimensions(); | |
const {showMenuContext, setShowMenuContext}:any = useContext(CONTEXT) | |
const {themeTypeContext, setThemeTypeContext}:any = useContext(CONTEXT) | |
const {showCloudflareTurnstileContext, setShowCloudflareTurnstileContext}:any = useContext(CONTEXT) | |
const [isReady, setIsReady] = useState(false); | |
const [isError, setIsError] = useState({state:false,text:""}); | |
const [isNavigate, setIsNavigate] = useState(false); | |
const image = useRef<any>(null); | |
const image_layout = useRef<any>(null); | |
useEffect(()=>{(async () => { | |
if (item.type === "page"){ | |
const store_chapter_image_data = await ChapterDataStorage.get(item.id) | |
if (store_chapter_image_data){ | |
image.current = {type:"blob",data:store_chapter_image_data.data} | |
image_layout.current = store_chapter_image_data.layout | |
setIsReady(true) | |
}else{ | |
setIsError({state:true,text:"Image not found!"}) | |
} | |
}else setIsReady(true) | |
})()},[]) | |
useFocusEffect(useCallback(() => { | |
return () => { | |
image.current = null | |
}; | |
},[])) | |
return ( <Pressable | |
onPress={()=>{ | |
setShowOptions({type:"general",state:!showOptions.state}) | |
}} | |
style={{ | |
display:"flex", | |
width:"100%", | |
height:"auto", | |
borderWidth:0, | |
alignItems:"center", | |
}} | |
> | |
<>{isReady | |
? (<> | |
{item.type === "page" && ( | |
<Image source={image.current} | |
contentFit="contain" | |
style={{ | |
width:Dimensions.width > 720 | |
? 0.8 * Dimensions.width * (1 - zoom / 100) | |
: `${100 - zoom}%`, | |
aspectRatio: image_layout.current.width / image_layout.current.height, | |
}} | |
onLoadEnd={()=>{ | |
image.current = "" | |
}} | |
/> | |
)} | |
{item.type === "chapter-info-banner" && ( | |
<View | |
style={{ | |
display:"flex", | |
flexDirection:"column", | |
alignItems:"center", | |
gap:12, | |
width:Dimensions.width > 720 | |
? 0.8 * Dimensions.width * (1 - zoom / 100) | |
: `${100 - zoom}%`, | |
height:"auto", | |
backgroundColor:"black", | |
padding:16, | |
}} | |
> | |
<Text selectable={false} | |
numberOfLines={1} | |
style={{ | |
color:Theme[themeTypeContext].text_color, | |
fontSize:(0.03 * ((Dimensions.width+Dimensions.height)/2)) * (1 - zoom/100), | |
fontFamily:"roboto-bold", | |
}} | |
> | |
End: {item.value.last} | |
</Text> | |
<Text selectable={false} | |
numberOfLines={1} | |
style={{ | |
color:Theme[themeTypeContext].text_color, | |
fontSize:(0.03 * ((Dimensions.width+Dimensions.height)/2)) * (1 - zoom/100), | |
fontFamily:"roboto-bold", | |
}} | |
> | |
Next: {item.value.next} | |
</Text> | |
</View> | |
)} | |
{item.type === "no-chapter-banner" && ( | |
<View | |
style={{ | |
display:"flex", | |
flexDirection:"column", | |
alignItems:"center", | |
gap:12, | |
width:Dimensions.width > 720 | |
? 0.8 * Dimensions.width * (1 - zoom / 100) | |
: `${100 - zoom}%`, | |
height:"auto", | |
backgroundColor:"black", | |
padding:16, | |
}} | |
> | |
<Text selectable={false} | |
numberOfLines={1} | |
style={{ | |
color:Theme[themeTypeContext].text_color, | |
fontSize:(0.03 * ((Dimensions.width+Dimensions.height)/2)) * (1 - zoom/100), | |
fontFamily:"roboto-bold", | |
}} | |
> | |
No more chapters on local. | |
</Text> | |
<Text selectable={false} | |
numberOfLines={1} | |
style={{ | |
color:Theme[themeTypeContext].text_color, | |
fontSize:(0.03 * ((Dimensions.width+Dimensions.height)/2)) * (1 - zoom/100), | |
fontFamily:"roboto-bold", | |
}} | |
> | |
You can go back and download more if available. | |
</Text> | |
</View> | |
)} | |
{item.type === "chapter-navigate" && ( | |
<View | |
style={{ | |
display:"flex", | |
flexDirection:"row", | |
justifyContent:"space-between", | |
alignItems:"center", | |
width:Dimensions.width > 720 | |
? 0.8 * Dimensions.width * (1 - zoom / 100) | |
: `${100 - zoom}%`, | |
paddingHorizontal: 12, | |
paddingVertical: 18, | |
}} | |
> | |
<TouchableRipple disabled={isNavigate} | |
rippleColor={Theme[themeTypeContext].ripple_color_outlined} | |
style={{ | |
width:"auto", | |
display:"flex", | |
flexDirection:"column", | |
justifyContent:"center", | |
alignSelf:"center", | |
padding:8, | |
paddingHorizontal:18, | |
borderRadius:Dimensions.width*0.65/2, | |
backgroundColor:Theme[themeTypeContext].border_color, | |
shadowColor: Theme[themeTypeContext].shadow_color, | |
shadowOffset: { width: 0, height: 2 }, | |
shadowOpacity: 0.25, | |
shadowRadius: 3.84, | |
elevation: 5, | |
}} | |
onPress={async ()=>{ | |
setIsNavigate(true); | |
const stored_chapter_info = await ChapterStorage.getByIdx(`${SOURCE}-${COMIC_ID}`,item.chapter_idx-1) | |
if (stored_chapter_info?.data_state === "completed"){ | |
setIsLoading(true); | |
router.replace(`/read/${SOURCE}/${COMIC_ID}/${stored_chapter_info.idx}/`) | |
}else{ | |
Toast.show({ | |
type: 'info', | |
text1: 'Chapter not download yet.', | |
text2: "You can go back and download more.", | |
position: "bottom", | |
visibilityTime: 4000, | |
text1Style:{ | |
fontFamily:"roboto-bold", | |
fontSize:((Dimensions.width+Dimensions.height)/2)*0.025 | |
}, | |
text2Style:{ | |
fontFamily:"roboto-medium", | |
fontSize:((Dimensions.width+Dimensions.height)/2)*0.0185, | |
}, | |
}); | |
} | |
setIsNavigate(false); | |
}} | |
> | |
<Text selectable={false} | |
style={{ | |
color:Theme[themeTypeContext].text_color, | |
fontFamily:"roboto-medium", | |
fontSize:(Dimensions.width+Dimensions.height)/2*0.03 * (1 - zoom / 100) | |
}} | |
>Previous</Text> | |
</TouchableRipple> | |
<TouchableRipple disabled={isNavigate} | |
rippleColor={Theme[themeTypeContext].ripple_color_outlined} | |
style={{ | |
width:"auto", | |
display:"flex", | |
flexDirection:"column", | |
justifyContent:"center", | |
alignSelf:"center", | |
padding:8, | |
paddingHorizontal:18, | |
borderRadius:Dimensions.width*0.65/2, | |
backgroundColor:Theme[themeTypeContext].border_color, | |
shadowColor: Theme[themeTypeContext].shadow_color, | |
shadowOffset: { width: 0, height: 2 }, | |
shadowOpacity: 0.25, | |
shadowRadius: 3.84, | |
elevation: 5, | |
}} | |
onPress={async ()=>{ | |
setIsNavigate(true); | |
const stored_chapter_info = await ChapterStorage.getByIdx(`${SOURCE}-${COMIC_ID}`,item.chapter_idx+1) | |
if (stored_chapter_info?.data_state === "completed"){ | |
setIsLoading(true) | |
const stored_comic = await ComicStorage.getByID(SOURCE, COMIC_ID) | |
if (stored_comic.history.idx && item.chapter_idx+1 > stored_comic.history.idx) { | |
await ComicStorage.updateHistory(SOURCE, COMIC_ID, {idx:stored_chapter_info?.idx, id:stored_chapter_info?.id, title:stored_chapter_info?.title}) | |
} | |
router.replace(`/read/${SOURCE}/${COMIC_ID}/${stored_chapter_info.idx}/`) | |
}else{ | |
Toast.show({ | |
type: 'info', | |
text1: 'Chapter not download yet.', | |
text2: "You can go back and download more.", | |
position: "bottom", | |
visibilityTime: 4000, | |
text1Style:{ | |
fontFamily:"roboto-bold", | |
fontSize:((Dimensions.width+Dimensions.height)/2)*0.025 | |
}, | |
text2Style:{ | |
fontFamily:"roboto-medium", | |
fontSize:((Dimensions.width+Dimensions.height)/2)*0.0185, | |
}, | |
}); | |
} | |
setIsNavigate(false); | |
}} | |
> | |
<Text selectable={false} | |
style={{ | |
color:Theme[themeTypeContext].text_color, | |
fontFamily:"roboto-medium", | |
fontSize:(Dimensions.width+Dimensions.height)/2*0.03 * (1 - zoom / 100) | |
}} | |
>Next</Text> | |
</TouchableRipple> | |
</View> | |
)} | |
</>) | |
: ( | |
<View | |
style={{ | |
display:"flex", | |
justifyContent:"center", | |
alignItems:"center", | |
backgroundColor:Theme[themeTypeContext].background_color, | |
width:Dimensions.width > 720 | |
? 0.8 * Dimensions.width * (1 - zoom / 100) | |
: `${100 - zoom}%`, | |
height: Dimensions.height * 0.75 | |
}} | |
> | |
{isError.state | |
? ( | |
<View | |
style={{ | |
display:'flex', | |
flexDirection:"column", | |
justifyContent:"center", | |
alignItems:"center", | |
width:"100%", | |
height:"100%", | |
gap:12, | |
}} | |
> | |
<Icon source={"alert-circle"} size={25} color={"red"}/> | |
<Text style={{color:"white",fontSize:12,fontFamily:"roboto-bold"}}>{isError.text}</Text> | |
</View> | |
) | |
: (<ActivityIndicator animating={true}/>) | |
} | |
</View> | |
) | |
}</> | |
</Pressable> | |
) | |
} | |
export default ChapterImage |