settingsService.ts
1 import AsyncStorage from '@react-native-async-storage/async-storage'; 2 import { setMangaData, getMangaData } from './bookmarkService'; 3 import { fetchMangaDetails } from './mangaFireService'; 4 import { imageCache } from './CacheImages'; 5 6 interface AppSettings { 7 theme: 'light' | 'dark' | 'system'; 8 enableDebugTab: boolean; 9 onboardingCompleted: boolean; 10 accentColor?: string | undefined; 11 } 12 13 const SETTINGS_KEY = 'app_settings'; 14 15 export async function getAppSettings(): Promise<AppSettings> { 16 try { 17 const settingsStr = await AsyncStorage.getItem(SETTINGS_KEY); 18 if (settingsStr) { 19 return JSON.parse(settingsStr); 20 } 21 return { 22 theme: 'system', 23 enableDebugTab: false, 24 onboardingCompleted: false, 25 accentColor: undefined, 26 }; 27 } catch (error) { 28 console.error('Error getting app settings:', error); 29 return { 30 theme: 'system', 31 enableDebugTab: false, 32 onboardingCompleted: false, 33 accentColor: undefined, 34 }; 35 } 36 } 37 38 export async function setAppSettings(settings: AppSettings): Promise<void> { 39 try { 40 await AsyncStorage.setItem(SETTINGS_KEY, JSON.stringify(settings)); 41 } catch (error) { 42 console.error('Error saving app settings:', error); 43 } 44 } 45 46 export async function getDebugTabEnabled(): Promise<boolean> { 47 const settings = await getAppSettings(); 48 return settings.enableDebugTab; 49 } 50 51 export async function setDebugTabEnabled(enabled: boolean): Promise<void> { 52 const settings = await getAppSettings(); 53 settings.enableDebugTab = enabled; 54 await setAppSettings(settings); 55 } 56 57 export async function isOnboardingCompleted(): Promise<boolean> { 58 const settings = await getAppSettings(); 59 return settings.onboardingCompleted; 60 } 61 62 export async function setOnboardingCompleted( 63 completed: boolean 64 ): Promise<void> { 65 const settings = await getAppSettings(); 66 settings.onboardingCompleted = completed; 67 await setAppSettings(settings); 68 } 69 70 export async function exportAppData() { 71 const allKeys = await AsyncStorage.getAllKeys(); 72 const allPairs = await AsyncStorage.multiGet(allKeys); 73 const exportData: Record<string, any> = {}; 74 75 allPairs.forEach(([key, value]) => { 76 if (value) { 77 try { 78 exportData[key] = JSON.parse(value); 79 } catch { 80 exportData[key] = value; 81 } 82 } 83 }); 84 85 return exportData; 86 } 87 88 export async function importAppData(data: Record<string, any>) { 89 await AsyncStorage.clear(); 90 const pairs: [string, string][] = Object.entries(data).map(([key, value]) => [ 91 key, 92 typeof value === 'string' ? value : JSON.stringify(value), 93 ]); 94 await AsyncStorage.multiSet(pairs); 95 } 96 97 export async function clearAppData() { 98 await AsyncStorage.clear(); 99 } 100 101 export async function refreshMangaImages(): Promise<{ 102 success: boolean; 103 message: string; 104 }> { 105 try { 106 const allKeys = await AsyncStorage.getAllKeys(); 107 const mangaKeys = allKeys.filter((key) => key.startsWith('manga_')); 108 let updatedCount = 0; 109 110 // Clear the image cache before starting refresh 111 await imageCache.clearCache(); 112 113 for (const key of mangaKeys) { 114 const mangaId = key.replace('manga_', ''); 115 const mangaData = await getMangaData(mangaId); 116 117 if (mangaData) { 118 try { 119 const newMangaDetails = await fetchMangaDetails(mangaId); 120 if (newMangaDetails?.bannerImage) { 121 await setMangaData({ 122 ...mangaData, 123 bannerImage: newMangaDetails.bannerImage, 124 lastUpdated: Date.now(), 125 }); 126 updatedCount++; 127 } 128 } catch (error) { 129 console.error(`Error updating manga ${mangaId}:`, error); 130 } 131 } 132 } 133 134 return { 135 success: true, 136 message: `Updated images for ${updatedCount} manga out of ${mangaKeys.length} total`, 137 }; 138 } catch (error) { 139 console.error('Error refreshing manga images:', error); 140 return { 141 success: false, 142 message: error instanceof Error ? error.message : 'Unknown error', 143 }; 144 } 145 } 146 147 export async function migrateToNewStorage(): Promise<{ 148 success: boolean; 149 message: string; 150 }> { 151 try { 152 // Get all keys 153 const allKeys = await AsyncStorage.getAllKeys(); 154 155 // Get bookmark keys 156 const bookmarkKeys = allKeys.filter((key) => key.startsWith('bookmark_')); 157 158 // Clear image cache before migration 159 await imageCache.clearCache(); 160 161 // Process each bookmark 162 for (const bookmarkKey of bookmarkKeys) { 163 const id = bookmarkKey.replace('bookmark_', ''); 164 165 // Get all related data 166 const [bookmarkStatus, title, imageUrl, readChaptersStr] = 167 await AsyncStorage.multiGet([ 168 bookmarkKey, 169 `title_${id}`, 170 `image_${id}`, 171 `manga_${id}_read_chapters`, 172 ]); 173 174 // Parse read chapters 175 const readChapters = readChaptersStr?.[1] 176 ? JSON.parse(readChaptersStr[1]) 177 : []; 178 179 // Fetch latest manga details 180 const mangaDetails = await fetchMangaDetails(id); 181 182 // Create new manga data structure 183 await setMangaData({ 184 id, 185 title: mangaDetails?.title || title?.[1] || '', 186 bannerImage: mangaDetails?.bannerImage || imageUrl?.[1] || '', 187 bookmarkStatus: (bookmarkStatus?.[1] as any) || null, 188 readChapters, 189 lastReadChapter: 190 readChapters.length > 0 191 ? readChapters[readChapters.length - 1] 192 : undefined, 193 lastUpdated: Date.now(), 194 }); 195 196 // Delete old data 197 await AsyncStorage.multiRemove([ 198 bookmarkKey, 199 `title_${id}`, 200 `image_${id}`, 201 `manga_${id}_read_chapters`, 202 ]); 203 } 204 205 return { 206 success: true, 207 message: `Successfully migrated ${bookmarkKeys.length} manga to new storage format`, 208 }; 209 } catch (error) { 210 console.error('Error during migration:', error); 211 return { 212 success: false, 213 message: `Migration failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 214 }; 215 } 216 }