Sheets via neuer KeyboardAwareSheet-Composable (in Modal pattern, auto-grow mit Tastatur, paddingBottom-Lift): EditMail, AddDomain, CreateRoom, ConnectMail. GameOverScreen behält Spring-Slide-In, nutzt RN Keyboard.addListener für Lift. - KeyboardAwareSheet.tsx — universal modal with sheet-grow + keyboard-padding - react-native-keyboard-controller installiert + KeyboardProvider in Root - Snake: time + ScoreProgressBar + useSnakeSounds (haptic, audio TODO) - Tetris: title weg, Buttons zentriert, kein Pressable mit style-fn - DPad-Buttons 60→48, more bg, no scale - useMe: pub-sub listener pattern für app-weite avatar/nickname-Updates - dm.tsx: resolveAvatar wrap (iron.png-Warning) - Mail-error-humanizer + locales Recovery-Doc-Update in docs/internal/RECOVERY_LOG_2026-05-10.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
68 lines
2.5 KiB
TypeScript
68 lines
2.5 KiB
TypeScript
import { ReactNode } from 'react';
|
|
import { Platform, ScrollView, StyleProp, ViewStyle } from 'react-native';
|
|
import { useKeyboardHeight } from '../hooks/useKeyboardHeight';
|
|
|
|
/**
|
|
* Universal-Wrapper für Forms/Pages mit TextInput.
|
|
*
|
|
* Für Vollbild-Formulare (Auth, Profile-Edit) reicht das alleine:
|
|
* - iOS: `automaticallyAdjustKeyboardInsets` (iOS 14+) verschiebt focused Input aktiv.
|
|
* - Android: `paddingBottom: keyboardHeight` + `windowSoftInputMode=adjustResize`
|
|
* im Manifest.
|
|
*
|
|
* Für FIXED-HEIGHT Sheets/Modals reicht das nicht — der Sheet selbst muss
|
|
* zusätzlich nach oben verschoben werden. Pattern:
|
|
* ```tsx
|
|
* const keyboardHeight = useKeyboardHeight();
|
|
* <Animated.View style={{
|
|
* transform: [{ translateY: ... }],
|
|
* marginBottom: keyboardHeight, // lift sheet above keyboard
|
|
* }}>
|
|
* <KeyboardAdjustedView>
|
|
* {form content}
|
|
* </KeyboardAdjustedView>
|
|
* </Animated.View>
|
|
* ```
|
|
*
|
|
* Siehe `EditMailAccountSheet.tsx` für vollständiges Sheet-Pattern.
|
|
*
|
|
* Anti-Pattern: KeyboardAvoidingView mit `behavior="padding"` greift bei
|
|
* Vollbild-Layouts mit `paddingTop: insets.top` nicht — siehe
|
|
* `docs/internal/RECOVERY_LOG_2026-05-10.md` §7.2.
|
|
*/
|
|
export interface KeyboardAdjustedViewProps {
|
|
children: ReactNode;
|
|
/** Style für den ScrollView (outer container). */
|
|
style?: StyleProp<ViewStyle>;
|
|
/** Style für den ScrollView-Inhalt (Padding gehört hier rein, nicht in `style`). */
|
|
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
/** Extra Padding bottom on top of keyboard height (z.B. wenn fixed CTA-Bar drüber sitzt). */
|
|
extraBottomOffset?: number;
|
|
/** Default 'handled' — Tap auf nicht-Input-Bereich schließt Keyboard. */
|
|
keyboardShouldPersistTaps?: 'always' | 'never' | 'handled';
|
|
}
|
|
|
|
export function KeyboardAdjustedView({
|
|
children,
|
|
style,
|
|
contentContainerStyle,
|
|
extraBottomOffset = 0,
|
|
keyboardShouldPersistTaps = 'handled',
|
|
}: KeyboardAdjustedViewProps) {
|
|
const keyboardHeight = useKeyboardHeight();
|
|
const bottomPad = keyboardHeight > 0 ? keyboardHeight + extraBottomOffset : 0;
|
|
|
|
return (
|
|
<ScrollView
|
|
style={style}
|
|
contentContainerStyle={[contentContainerStyle, { paddingBottom: bottomPad }]}
|
|
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
|
|
keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}
|
|
automaticallyAdjustKeyboardInsets={Platform.OS === 'ios'}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{children}
|
|
</ScrollView>
|
|
);
|
|
}
|