/ services / settingsService.ts
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  }