feat(blocker): reactivation only re-arms the VPN/filter; a11y setup is first-time-only
The a11y (App-Lock) permission flow now runs only the first time the user turns
protection on. Reactivating after a cooldown / external disable just re-starts the
VPN/DNS filter — no a11y system prompt, no modal loop ("a11y can't be activated…").
- blocker.tsx handleActivateFamilyControls: no error modal when error === 'accessibility_pending'
(we just opened the a11y settings — that's the feedback; tapping again re-opens, no loop).
- lib/protection.ts getCombinedState: "active" = urlFilter on (App-Lock is optional hardening,
not a precondition); "recoveringFromBypass" now means urlFilter is OFF while the backend
says it should be on (a real bypass), instead of "lock is off".
- blocker.tsx recoveringFromBypass alert: offers "turn back on" → activateUrlFilter (VPN),
not activateFamilyControls.
- _layout.tsx bypass re-arm (enforceProtection fallback + onBypassNotificationTap):
protection.activate() instead of activateFamilyControls().
- new i18n keys: blocker.protection_off_title / protection_off_message / reactivate_btn.
JS-only (hot-reloadable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3c2aee7bda
commit
4492c7b265
@ -92,10 +92,12 @@ export default function AppLayout() {
|
||||
bypassNotifiedRef.current = true;
|
||||
const notified = await notifyBypassDetected();
|
||||
if (!notified) {
|
||||
// Fallback wenn Notifications nicht erlaubt sind.
|
||||
// Fallback wenn Notifications nicht erlaubt sind. Reaktivierung setzt
|
||||
// NUR den Filter/VPN wieder — kein a11y-Prompt (das passiert nur beim
|
||||
// ersten Einrichten).
|
||||
rearmInFlightRef.current = true;
|
||||
router.replace('/blocker');
|
||||
await protection.activateFamilyControls().catch(() => ({ enabled: false }));
|
||||
await protection.activate().catch(() => null);
|
||||
}
|
||||
} finally {
|
||||
rearmInFlightRef.current = false;
|
||||
@ -107,7 +109,8 @@ export default function AppLayout() {
|
||||
rearmInFlightRef.current = true;
|
||||
try {
|
||||
router.replace('/blocker');
|
||||
await protection.activateFamilyControls().catch(() => ({ enabled: false }));
|
||||
// Reaktivierung = nur Filter/VPN wieder setzen (a11y nur beim ersten Mal).
|
||||
await protection.activate().catch(() => null);
|
||||
} finally {
|
||||
rearmInFlightRef.current = false;
|
||||
}
|
||||
|
||||
@ -134,12 +134,17 @@ export default function BlockerScreen() {
|
||||
try {
|
||||
const result = await activateFamilyControls();
|
||||
console.log('[blocker] activateFamilyControls:', result);
|
||||
if (!result.enabled) {
|
||||
// `accessibility_pending` = die a11y-Berechtigung fehlt noch und wir haben
|
||||
// grad die System-Settings geöffnet → das IST das Feedback. Kein Fehler-
|
||||
// Modal (sonst Modal-Loop bei jedem Tap). a11y wird nur beim ersten
|
||||
// Einrichten geholt; danach ist das hier ein 1-Tap-Arm ohne Dialog.
|
||||
if (!result.enabled && result.error !== 'accessibility_pending') {
|
||||
Alert.alert(
|
||||
t('blocker.activate_app_lock_failed_title'),
|
||||
result.error ?? t('blocker.activate_app_lock_failed_msg'),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch (e: any) {
|
||||
console.error('[blocker] activateFamilyControls threw:', e);
|
||||
Alert.alert(t('blocker.activation_failed_title'), e?.message ?? t('common.unknown_error'));
|
||||
@ -188,16 +193,15 @@ export default function BlockerScreen() {
|
||||
}
|
||||
if (bypassAlertShownRef.current) return;
|
||||
bypassAlertShownRef.current = true;
|
||||
// Schutz-Filter ist aus, sollte aber an sein → Reaktivierung setzt NUR den
|
||||
// VPN/Filter wieder (kein a11y-Prompt — das passiert nur beim ersten Mal).
|
||||
Alert.alert(
|
||||
t('blocker.activate_app_lock_failed_title'),
|
||||
t('blocker.layers_app_lock_warning'),
|
||||
[{
|
||||
text: t('common.ok'),
|
||||
onPress: () => {
|
||||
void handleActivateFamilyControls();
|
||||
},
|
||||
}],
|
||||
{ cancelable: false },
|
||||
t('blocker.protection_off_title'),
|
||||
t('blocker.protection_off_message'),
|
||||
[
|
||||
{ text: t('common.ok'), style: 'cancel' },
|
||||
{ text: t('blocker.reactivate_btn'), onPress: () => { void handleActivateUrlFilter(); } },
|
||||
],
|
||||
);
|
||||
}, [state?.phase, t]);
|
||||
|
||||
|
||||
@ -243,16 +243,17 @@ export const protection = {
|
||||
} as DeviceLayers)
|
||||
: rawLayers;
|
||||
|
||||
const allLayersOn = isAllLayersOn(layers);
|
||||
const iosLockActive =
|
||||
layers.appDeletionLock ?? layers.familyControls ?? false;
|
||||
// "Aktiv" = der eigentliche Schutz (URL-/DNS-Filter) läuft. Der App-Lock
|
||||
// (familyControls/tamperLock) ist optionales Hardening — er macht den Schutz
|
||||
// schwerer abschaltbar, ist aber keine Voraussetzung für "geschützt". Er wird
|
||||
// nur beim ersten Aktivieren eingerichtet; eine Reaktivierung setzt nur den
|
||||
// Filter wieder. → "recoveringFromBypass" heißt deshalb: Filter ist aus,
|
||||
// obwohl das Backend sagt er sollte an sein (= jemand hat den VPN extern aus).
|
||||
const phase: ProtectionPhase = cooldown.active
|
||||
? "cooldownActive"
|
||||
: backend?.protectionShouldBeActive === true &&
|
||||
layers.urlFilter === true &&
|
||||
iosLockActive !== true
|
||||
: backend?.protectionShouldBeActive === true && layers.urlFilter !== true
|
||||
? "recoveringFromBypass"
|
||||
: allLayersOn
|
||||
: layers.urlFilter === true
|
||||
? "active"
|
||||
: "inactive";
|
||||
|
||||
|
||||
@ -241,6 +241,9 @@
|
||||
"activate_url_failed_title": "URL-Filter konnte nicht aktiviert werden",
|
||||
"activate_url_failed_msg": "Unbekannter Fehler.\nDu kannst es nochmal versuchen oder System-Einstellungen prüfen.",
|
||||
"activate_settings_btn": "Einstellungen",
|
||||
"protection_off_title": "Schutz ist aus",
|
||||
"protection_off_message": "Der Filter läuft gerade nicht, sollte aber an sein. Willst du ihn wieder einschalten?",
|
||||
"reactivate_btn": "Wieder einschalten",
|
||||
"activate_app_lock_failed_title": "App-Lock konnte nicht aktiviert werden",
|
||||
"activate_app_lock_failed_msg": "Die nötige Berechtigung wurde verweigert. Du kannst es nochmal versuchen.",
|
||||
"sync_list_failed_title": "Filter-Liste konnte nicht geladen werden",
|
||||
|
||||
@ -241,6 +241,9 @@
|
||||
"activate_url_failed_title": "Could not activate URL filter",
|
||||
"activate_url_failed_msg": "Unknown error.\nYou can try again or check System Settings.",
|
||||
"activate_settings_btn": "Settings",
|
||||
"protection_off_title": "Protection is off",
|
||||
"protection_off_message": "The filter isn't running but should be. Want to turn it back on?",
|
||||
"reactivate_btn": "Turn back on",
|
||||
"activate_app_lock_failed_title": "Could not activate App Lock",
|
||||
"activate_app_lock_failed_msg": "The required permission was denied. You can try again.",
|
||||
"sync_list_failed_title": "Filter list could not be loaded",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user