Android-Onboarding (Platform.OS dispatch in ProtectionSlide):
- Neue Phasen für Android: preexplain_vpn → preexplain_a11y → a11y_pending
- AppState-Listener: nach Settings-Rückkehr auto-poll isAccessibilityEnabled
→ wenn live, armTamperLock + finish (kein Fokus-Klick nötig)
- onboardingAssets: 8 neue Mappings (android_vpn + android_a11y × 4 Locales)
- Screenshots: vpn-permission + a11y-rebreak-row pro Locale
- Locale-Keys: protection_url_android, protection_lock_android, cta_open_a11y,
cta_check_a11y, dialog_button_vpn_ok, dialog_button_a11y_toggle, tap_marker_hint_*
Lyra-Post i18n Phase 1 (Scaffold, feature-flag OFF by default):
- schema.prisma: CommunityPost.i18nKey String? (nullable)
- migration 20260517_add_lyra_post_i18n_key: ALTER TABLE ADD COLUMN i18n_key
(NICHT auto-deployed — `prisma migrate deploy` als separater Step)
- server/lib/lyraPostCatalog.ts: 15 Templates skelettiert + pickRandomTemplate
- cron/lyra-post: USE_TEMPLATE_CATALOG=true Branch → speichert i18nKey;
default false → LLM-Path unverändert (zero-risk-deployment)
- community.createPost: optionaler i18nKey-Parameter
- posts.get: i18nKey in API-Response
- PostCard: 3-Zeilen-Branch — i18nKey ? t('lyra_posts.'+id) : content
- stores/community: i18nKey?: string|null im Interface
- de.json: lyra_posts-Block mit 15 IDs + DE-Texten
Single-Banner-Verhalten auf Android verifiziert:
lockedIn=urlFilter && appDeletionLock funktioniert weiter — auf Android
alias appDeletionLock ← tamperLock; onboarding arms tamperLock, also
nach onboarding-done direkt ProtectionLockedCard sichtbar.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
85 lines
3.4 KiB
TypeScript
85 lines
3.4 KiB
TypeScript
/**
|
|
* 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.
|
|
*
|
|
* Dialog-Typen:
|
|
* iOS:
|
|
* - `url_filter` → NEFilter-System-Dialog ("Erlauben"-Button)
|
|
* - `screen_time` → Family-Controls-/Screen-Time-Dialog ("Fortfahren")
|
|
* Android:
|
|
* - `android_vpn` → VpnService-System-Dialog ("OK"-Button)
|
|
* - `android_a11y` → Bedienungshilfen-Settings mit ReBreak-Eintrag
|
|
*
|
|
* Diese Maps explizit mit `require(...)` deklarieren — RN/Metro kann keine
|
|
* dynamischen Pfade auflösen.
|
|
*/
|
|
|
|
type Dialog = 'url_filter' | 'screen_time' | 'android_vpn' | 'android_a11y';
|
|
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');
|
|
|
|
// Android — VpnService-Permission-Dialog ("Verbindungsanforderung")
|
|
const ANDROID_VPN_DE = require('../assets/onboarding/de/android-vpn-permission-001.png');
|
|
const ANDROID_VPN_EN = require('../assets/onboarding/en/android-vpn-permission-001.png');
|
|
const ANDROID_VPN_FR = require('../assets/onboarding/fr/android-vpn-permission-001.png');
|
|
const ANDROID_VPN_AR = require('../assets/onboarding/ar/android-vpn-permission-001.png');
|
|
|
|
// Android — Accessibility-Settings, ReBreak-Row sichtbar
|
|
const ANDROID_A11Y_DE = require('../assets/onboarding/de/android-a11y-rebreak-row-001.png');
|
|
const ANDROID_A11Y_EN = require('../assets/onboarding/en/android-a11y-rebreak-row-001.png');
|
|
const ANDROID_A11Y_FR = require('../assets/onboarding/fr/android-a11y-rebreak-row-001.png');
|
|
const ANDROID_A11Y_AR = require('../assets/onboarding/ar/android-a11y-rebreak-row-001.png');
|
|
/* 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,
|
|
},
|
|
android_vpn: {
|
|
de: ANDROID_VPN_DE,
|
|
en: ANDROID_VPN_EN,
|
|
fr: ANDROID_VPN_FR,
|
|
ar: ANDROID_VPN_AR,
|
|
},
|
|
android_a11y: {
|
|
de: ANDROID_A11Y_DE,
|
|
en: ANDROID_A11Y_EN,
|
|
fr: ANDROID_A11Y_FR,
|
|
ar: ANDROID_A11Y_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!;
|
|
}
|