rebreak-monorepo/apps/rebreak-native/lib/onboardingAssets.ts
chahinebrini 33aa3464b8 feat(onboarding): protection pointer redesign + i18n screenshots + lockedIn fix
## Protection Pre-Explainer: External Pointer

Vorher: Pulse-Ring absolute-positioniert IM Screenshot — Position musste
per-locale fine-tuned werden weil Apple-Dialog-Höhe variiert (DE/EN/FR/AR
haben unterschiedliche Text-Längen → Dialog hat verschiedene Höhen →
Erlauben-Button rutscht).

Jetzt: animierter Pfeil + Label-Pill UNTER dem Screenshot. Dimensions-
agnostic, funktioniert in allen 4 Sprachen ohne Locale-spezifische Magie.

- ScreenshotPointer komplett refactored: caret-up + bouncing pill mit
  Button-Label-Text (z.B. 'Tippe "Erlauben"' / 'Tap "Allow"' / etc.)
- onboardingAssets.ts: getPointerPosition deprecated/entfernt
- ProtectionSlide nutzt neue API mit buttonLabelKey
- 4 Locales: dialog_button_allow + dialog_button_continue
- tap_marker_hint refined (kein "roter Marker"-Ref mehr)

## i18n-aware Screenshots

en/fr/ar Permission-Dialog-Screenshots zur Map ergänzt. Resolver fällt
auf de zurück wenn andere Sprache fehlt.

## Dynamic Sizing

ProtectionSlide nutzt useWindowDimensions:
  height: min(320, max(200, screenH * 0.32))
→ passt auf iPhone SE (213px) bis Pro Max (320px capped) ohne Scroll.

OnboardingShell ScrollView-Padding reduziert (16→12 top, 24→16 bottom).
ProtectionSlide-Spacing tightened.

## Blocker: lockedIn Fix

Bug: `lockedIn = appDeletionLockActive` ignorierte URL-Filter-State —
wenn User nur FC aktivierte (ohne URL-Filter), zeigte App grünen "Schutz
aktiv"-Banner obwohl URL-Filter aus war. Fix:
  lockedIn = urlFilter && appDeletionLock
→ Beide müssen wirklich aktiv sein für den grünen Banner.

## LayerSwitchCard: lockedHint Prop

Optional Hint-Text der unter dem active Layer angezeigt wird, z.B.
"System-gesperrt. Nur in iOS-Einstellungen → Bildschirmzeit → Verwaltung
durch ReBreak deaktivierbar.". Wird für iOS App-Lock-Card genutzt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 19:58:56 +02:00

58 lines
2.3 KiB
TypeScript

/**
* iOS Permission-Dialog-Screenshots für den Onboarding-Pre-Explainer.
*
* Pro Sprache liegen die Screenshots in `assets/onboarding/<lang>/`. Falls
* eine Sprache nicht (oder noch nicht) verfügbar ist, fällt der Resolver auf
* `de` zurück.
*
* Diese Maps explizit mit `require(...)` deklarieren — RN/Metro kann keine
* dynamischen Pfade auflösen.
*/
type Dialog = 'url_filter' | 'screen_time';
type Lang = 'de' | 'en' | 'fr' | 'ar';
/* eslint-disable @typescript-eslint/no-require-imports */
const URL_FILTER_DE = require('../assets/onboarding/de/url_filter_permission.jpeg');
const URL_FILTER_EN = require('../assets/onboarding/en/url_filter_permission.jpeg');
const URL_FILTER_FR = require('../assets/onboarding/fr/url_filter_permission.jpeg');
const URL_FILTER_AR = require('../assets/onboarding/ar/url_filter_permission.jpeg');
const SCREEN_TIME_DE = require('../assets/onboarding/de/screen_time_permission.jpeg');
const SCREEN_TIME_EN = require('../assets/onboarding/en/screen_time_permission.jpeg');
const SCREEN_TIME_FR = require('../assets/onboarding/fr/screen_time_permission.jpeg');
const SCREEN_TIME_AR = require('../assets/onboarding/ar/screen_time_permission.jpeg');
/* eslint-enable @typescript-eslint/no-require-imports */
const SCREENSHOTS: Record<Dialog, Partial<Record<Lang, number>>> = {
url_filter: {
de: URL_FILTER_DE,
en: URL_FILTER_EN,
fr: URL_FILTER_FR,
ar: URL_FILTER_AR,
},
screen_time: {
de: SCREEN_TIME_DE,
en: SCREEN_TIME_EN,
fr: SCREEN_TIME_FR,
ar: SCREEN_TIME_AR,
},
};
/**
* Resolves the right screenshot for the current language, with de-fallback.
* Returns the result of `require(...)` (an opaque module-handle for Metro).
*/
export function getPermissionScreenshot(dialog: Dialog, lang: string): number {
const normalized = (
lang === 'de' || lang === 'en' || lang === 'fr' || lang === 'ar' ? lang : 'de'
) as Lang;
const map = SCREENSHOTS[dialog];
return map[normalized] ?? map.de!;
}
// (Deprecated) getPointerPosition entfernt — der Pointer wird jetzt extern
// UNTER dem Screenshot gerendert (ScreenshotPointer-Komponente), nicht mehr
// per-percent overlayed. Damit entfällt die Notwendigkeit pixel-genaue
// Positionen pro Locale + Dialog zu pflegen — siehe ScreenshotPointer.tsx.