import { useCallback, useState } from 'react'; import { Platform } from 'react-native'; import Constants from 'expo-constants'; import { supabase } from '../lib/supabase'; import { protection } from '../lib/protection'; type SyncResult = { ok: boolean; count?: number; plan?: string; error?: string }; /** * Synct die binary Blocklist (`blocklist.bin`) vom Server in die App-Group. * Die NEFilter-Extension memory-mapped diese Datei — ohne Sync = leere * Blocklist = nichts wird geblockt. * * Triggers: * - direkt nach activateUrlFilter() success * - nach Domain-Add/-Submit/-Delete * - bei App-Resume (in case Server-Updates kamen) * * Backend respondet 304 wenn ETag matched → kein Re-Download. * * iOS-Layer-2: am selben Trigger wird auch die kuratierte webContent-Gambling- * Domain-Liste vom Backend gesynct (`syncWebContentDomains` → * `webcontent-domains.json` im App-Group-Cache). Best-effort und entkoppelt: * solange der Layer-2-Endpoint nicht deployed ist, schlägt dieser Sync fehl — * das beeinflusst das Blocklist-Sync-Ergebnis NICHT (der native * loadWebContentDomains fällt sauber auf die gebündelte JSON zurück). */ export function useBlocklistSync() { const [syncing, setSyncing] = useState(false); const [lastResult, setLastResult] = useState(null); const sync = useCallback(async (): Promise => { if (syncing) return { ok: false, error: 'already_syncing' }; setSyncing(true); try { const baseURL = Constants.expoConfig?.extra?.apiUrl as string; const session = (await supabase.auth.getSession()).data.session; const authToken = session?.access_token; if (!baseURL || !authToken) { const result = { ok: false, error: 'missing_baseURL_or_token' }; setLastResult(result); return result; } // iOS-Layer-2: webContent-Domain-Liste am selben Trigger mitsyncen. // Bewusst NICHT awaited mit dem Blocklist-Sync gekoppelt — ein // Fehlschlag (z.B. Endpoint noch nicht deployed) darf das Blocklist- // Ergebnis nicht kippen. Fallback auf die gebündelte JSON greift nativ. if (Platform.OS === 'ios') { protection .syncWebContentDomains({ baseURL, authToken }) .then((res) => console.log('[webcontent-sync] ok:', JSON.stringify(res)), ) .catch((e: any) => console.warn( '[webcontent-sync] failed (bundled fallback active):', e?.message ?? e, ), ); } const res = await protection.syncBlocklist({ baseURL, authToken }); const result = { ok: true, count: res.count, plan: res.plan }; setLastResult(result); console.log('[blocklist-sync] ok:', res); return result; } catch (e: any) { const result = { ok: false, error: e?.message ?? 'sync_failed' }; setLastResult(result); console.error('[blocklist-sync] failed:', e); return result; } finally { setSyncing(false); } }, [syncing]); return { sync, syncing, lastResult }; }