TN-User fielen bisher mangels TN-Liste auf die DE-Liste zurück. Jetzt eigene (kurze) TN-Starter-Liste: mbet216.com, 2xbet365.com, cesar365.com, icombet.com, unibet365.net (von einem TN-Test-User gemeldet). TN in COUNTRY_KEYS (webcontent-Endpoint) + VIP_COUNTRIES (Geräte-Region- Auflösung + Add-Check). Native Region-Logik ist generisch (Locale.region → JSON-Key) — kein Native-Code nötig. gambling-domains.json _meta v3. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
import * as Localization from 'expo-localization';
|
|
import { apiFetch } from '../lib/api';
|
|
|
|
/**
|
|
* Landabhängige VIP-Layer-2-Liste.
|
|
*
|
|
* Das Backend (`GET /api/protection/webcontent-domains`) liefert die
|
|
* komponierte Liste pro Land (Custom-Domains gekappt auf 30 + kuratierte
|
|
* Auffüllung, dedup, Cap 50). Hier wählen wir die Country-Slice nach der
|
|
* GERÄTE-Region aus — dasselbe Signal, das auch der native iOS-webContent-
|
|
* Filter nutzt (`Locale.current.region`).
|
|
*/
|
|
|
|
const VIP_COUNTRIES = ['DE', 'GB', 'FR', 'TN'] as const;
|
|
export type VipCountry = (typeof VIP_COUNTRIES)[number];
|
|
|
|
/** Geräte-Region → unterstützter VIP-Ländercode. Fallback DE. */
|
|
export function resolveVipCountry(): VipCountry {
|
|
const region = Localization.getLocales()[0]?.regionCode?.toUpperCase();
|
|
if (region && (VIP_COUNTRIES as readonly string[]).includes(region)) {
|
|
return region as VipCountry;
|
|
}
|
|
return 'DE';
|
|
}
|
|
|
|
type WebContentResponse = { _meta?: unknown } & Record<string, string[]>;
|
|
|
|
export function useWebContentDomains() {
|
|
const [country] = useState<VipCountry>(resolveVipCountry);
|
|
const [domains, setDomains] = useState<string[] | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const mountedRef = useRef(true);
|
|
|
|
useEffect(() => {
|
|
mountedRef.current = true;
|
|
return () => {
|
|
mountedRef.current = false;
|
|
};
|
|
}, []);
|
|
|
|
const refetch = useCallback(async () => {
|
|
try {
|
|
const res = await apiFetch<WebContentResponse>(
|
|
'/api/protection/webcontent-domains',
|
|
);
|
|
if (!mountedRef.current) return;
|
|
const list = Array.isArray(res?.[country]) ? res[country] : [];
|
|
setDomains(list);
|
|
setError(null);
|
|
} catch (e: any) {
|
|
if (!mountedRef.current) return;
|
|
console.warn('[useWebContentDomains] fetch failed:', e?.message ?? e);
|
|
setError(e?.message ?? 'fetch_failed');
|
|
} finally {
|
|
if (mountedRef.current) setLoading(false);
|
|
}
|
|
}, [country]);
|
|
|
|
useEffect(() => {
|
|
refetch();
|
|
}, [refetch]);
|
|
|
|
return { country, domains, loading, error, refetch };
|
|
}
|