readChapterService.ts
1 import AsyncStorage from '@react-native-async-storage/async-storage'; 2 import { getMangaData, setMangaData } from './bookmarkService'; 3 import { MangaData } from '@/types'; 4 5 const LAST_READ_MANGA_KEY = 'last_read_manga'; 6 7 export interface LastReadManga { 8 id: string; 9 title: string; 10 chapterNumber: string; 11 timestamp: number; 12 } 13 14 export const getReadChapters = async (mangaId: string): Promise<string[]> => { 15 try { 16 const mangaData = await getMangaData(mangaId); 17 return mangaData?.readChapters || []; 18 } catch (error) { 19 console.error('Error getting read chapters:', error); 20 return []; 21 } 22 }; 23 24 export const getLastReadChapter = async ( 25 mangaId: string 26 ): Promise<string | null> => { 27 try { 28 const mangaData = await getMangaData(mangaId); 29 if (!mangaData?.readChapters || mangaData.readChapters.length === 0) { 30 return 'Not started'; 31 } 32 33 // Calculate the highest chapter number from readChapters array 34 const highestChapter = Math.max( 35 ...mangaData.readChapters.map((ch) => parseFloat(ch)) 36 ).toString(); 37 38 return `Chapter ${highestChapter}`; 39 } catch (error) { 40 console.error('Error getting last read chapter:', error); 41 return null; 42 } 43 }; 44 45 export const markChapterAsRead = async ( 46 mangaId: string, 47 chapterNumber: string, 48 currentReadChapters: string[] 49 ): Promise<string[]> => { 50 try { 51 const mangaData = await getMangaData(mangaId); 52 if (mangaData) { 53 const updatedReadChapters = Array.from( 54 new Set([...currentReadChapters, chapterNumber]) 55 ); 56 const highestChapter = Math.max( 57 ...updatedReadChapters.map((ch) => parseFloat(ch)) 58 ).toString(); 59 await setMangaData({ 60 ...mangaData, 61 readChapters: updatedReadChapters, 62 lastReadChapter: highestChapter, 63 lastUpdated: Date.now(), 64 }); 65 66 // Update last read manga info whenever a chapter is marked as read 67 await setLastReadManga(mangaId, mangaData.title, chapterNumber); 68 69 return updatedReadChapters; 70 } 71 return currentReadChapters; 72 } catch (error) { 73 console.error('Error marking chapter as read:', error); 74 return currentReadChapters; 75 } 76 }; 77 export const markChapterAsUnread = async ( 78 mangaId: string, 79 chapterNumber: string, 80 currentReadChapters: string[] 81 ): Promise<{ 82 updatedChapters: string[]; 83 newLastReadChapter: string | null; 84 }> => { 85 try { 86 const mangaData = await getMangaData(mangaId); 87 if (mangaData) { 88 const updatedReadChapters = currentReadChapters.filter( 89 (chapter) => chapter !== chapterNumber 90 ); 91 92 // Determine the new last read chapter 93 let newLastReadChapter: string | null = null; 94 if (updatedReadChapters.length > 0) { 95 newLastReadChapter = Math.max( 96 ...updatedReadChapters.map((ch) => parseFloat(ch)) 97 ).toString(); 98 } 99 100 // Update manga data with new read chapters and last read chapter 101 await setMangaData({ 102 ...mangaData, 103 readChapters: updatedReadChapters, 104 lastReadChapter: newLastReadChapter || '', 105 lastUpdated: Date.now(), 106 }); 107 108 // Update last read manga info if needed 109 const lastReadManga = await getLastReadManga(); 110 if ( 111 lastReadManga && 112 lastReadManga.id === mangaId && 113 lastReadManga.chapterNumber === chapterNumber 114 ) { 115 if (newLastReadChapter) { 116 await setLastReadManga(mangaId, mangaData.title, newLastReadChapter); 117 } else if (updatedReadChapters.length === 0) { 118 await setLastReadManga(mangaId, mangaData.title, 'not_started'); 119 } 120 } 121 122 return { 123 updatedChapters: updatedReadChapters, 124 newLastReadChapter, 125 }; 126 } 127 return { 128 updatedChapters: currentReadChapters, 129 newLastReadChapter: null, 130 }; 131 } catch (error) { 132 console.error('Error marking chapter as unread:', error); 133 return { 134 updatedChapters: currentReadChapters, 135 newLastReadChapter: null, 136 }; 137 } 138 }; 139 140 export const setLastReadManga = async ( 141 id: string, 142 title: string, 143 chapterNumber: string 144 ): Promise<void> => { 145 try { 146 const lastReadManga: LastReadManga = { 147 id, 148 title, 149 chapterNumber, 150 timestamp: Date.now(), 151 }; 152 153 console.log('Setting last read manga:', lastReadManga); 154 await AsyncStorage.setItem( 155 LAST_READ_MANGA_KEY, 156 JSON.stringify(lastReadManga) 157 ); 158 } catch (error) { 159 console.error('Error setting last read manga:', error); 160 } 161 }; 162 163 export const getRecentlyReadManga = async ( 164 limit: number = 6 165 ): Promise<MangaData[]> => { 166 try { 167 // Get all AsyncStorage keys 168 const allKeys = await AsyncStorage.getAllKeys(); 169 170 // Filter for manga data keys (manga_*) 171 const mangaKeys = allKeys.filter( 172 (key) => key.startsWith('manga_') && !key.includes('_read_chapters') 173 ); 174 175 const mangaDataArray = await Promise.all( 176 mangaKeys.map(async (key) => { 177 const data = await AsyncStorage.getItem(key); 178 return data ? (JSON.parse(data) as MangaData) : null; 179 }) 180 ); 181 182 const validManga = mangaDataArray.filter( 183 (manga): manga is MangaData => 184 manga !== null && 185 manga.readChapters && 186 manga.readChapters.length > 0 && 187 !!manga.bannerImage 188 ); 189 190 // Sort by lastUpdated timestamp (descending) 191 validManga.sort((a, b) => (b.lastUpdated || 0) - (a.lastUpdated || 0)); 192 193 // Return the top {limit} items 194 return validManga.slice(0, limit); 195 } catch (error) { 196 console.error('Error fetching recently read manga:', error); 197 return []; 198 } 199 }; 200 201 export const getLastReadManga = async (): Promise<LastReadManga | null> => { 202 try { 203 const data = await AsyncStorage.getItem(LAST_READ_MANGA_KEY); 204 if (!data) return null; 205 206 const parsedData = JSON.parse(data) as LastReadManga; 207 return parsedData; 208 } catch (error) { 209 console.error('Error getting last read manga:', error); 210 return null; 211 } 212 };