## 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>
66 lines
1.8 KiB
TypeScript
66 lines
1.8 KiB
TypeScript
import { ReactNode } from 'react';
|
|
import { ScrollView, View } from 'react-native';
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
import { useColors } from '../../lib/theme';
|
|
import { SlideProgress } from './SlideProgress';
|
|
|
|
/**
|
|
* Layout-Wrapper für alle Onboarding-Slides.
|
|
*
|
|
* ┌──────────────────────────────┐
|
|
* │ [Top-Padding + SafeArea] │
|
|
* │ ────── Progress-Bar ───── │
|
|
* │ │
|
|
* │ <children — ScrollView> │
|
|
* │ │
|
|
* │ ───── CTABar (sticky) ───── │
|
|
* └──────────────────────────────┘
|
|
*
|
|
* `cta` wird IM Shell gerendert (sticky-bottom + SafeArea). Slides müssen
|
|
* ihre Inhalte als `children` reinpacken (= scrollbarer Content-Bereich).
|
|
*/
|
|
export function OnboardingShell({
|
|
current,
|
|
total,
|
|
children,
|
|
cta,
|
|
}: {
|
|
current: number;
|
|
total: number;
|
|
children: ReactNode;
|
|
cta: ReactNode;
|
|
}) {
|
|
const colors = useColors();
|
|
const insets = useSafeAreaInsets();
|
|
|
|
return (
|
|
<View style={{ flex: 1, backgroundColor: colors.bg }}>
|
|
<View
|
|
style={{
|
|
paddingTop: insets.top + 12,
|
|
paddingHorizontal: 20,
|
|
paddingBottom: 8,
|
|
}}
|
|
>
|
|
<SlideProgress current={current} total={total} />
|
|
</View>
|
|
|
|
<ScrollView
|
|
style={{ flex: 1 }}
|
|
contentContainerStyle={{
|
|
paddingHorizontal: 20,
|
|
paddingTop: 12,
|
|
paddingBottom: 16,
|
|
flexGrow: 1,
|
|
}}
|
|
keyboardShouldPersistTaps="handled"
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{children}
|
|
</ScrollView>
|
|
|
|
{cta}
|
|
</View>
|
|
);
|
|
}
|