chahinebrini 14452b2a46 refactor(native): Pressable → TouchableOpacity sweep (style-fn swallows Android styles)
Alle <Pressable style={({pressed}) => ({...})}> ersetzt — style-Funktion
droppt auf Android (New Arch) intermittierend width/height, führt zu 0×0
unsichtbaren Elementen. TouchableOpacity mit activeOpacity ist stabil.

Außerdem übrige Pressables (plain style) aus components/ und app/
migriert sowie zwei überschüssige </View>-Tags in chat.tsx + RoomCard.tsx
entfernt die TS-Fehler verursacht haben.

64 Dateien, typecheck sauber.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:43:10 +02:00

275 lines
7.3 KiB
TypeScript

import { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { useTranslation } from 'react-i18next';
import { apiFetch } from '../../lib/api';
import { useColors } from '../../lib/theme';
import { KeyboardAwareSheet } from '../KeyboardAwareSheet';
const COLLAPSED_HEIGHT = 480;
type Props = {
visible: boolean;
onClose: () => void;
onCreated: (room: any) => void;
};
export function CreateRoomSheet({ visible, onClose, onCreated }: Props) {
const { t } = useTranslation();
const colors = useColors();
const styles = makeStyles(colors);
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [isPublic, setIsPublic] = useState(true);
const [joinMode, setJoinMode] = useState<'approval' | 'invite_only'>('approval');
const [creating, setCreating] = useState(false);
function reset() {
setName('');
setDescription('');
setIsPublic(true);
setJoinMode('approval');
}
function handleClose() {
reset();
onClose();
}
async function create() {
const trimmed = name.trim();
if (!trimmed || creating) return;
setCreating(true);
try {
const room = await apiFetch<any>('/api/chat/rooms', {
method: 'POST',
body: {
name: trimmed,
description: description.trim() || undefined,
isPublic,
joinMode: isPublic ? 'open' : joinMode,
},
});
onCreated(room);
reset();
onClose();
} catch (err: any) {
console.error('Room erstellen fehlgeschlagen:', err.message);
} finally {
setCreating(false);
}
}
return (
<KeyboardAwareSheet
visible={visible}
onClose={handleClose}
collapsedHeight={COLLAPSED_HEIGHT}
pushChildrenToBottom={false}
topRadius={22}
>
<View style={{ flex: 1, paddingHorizontal: 18, paddingTop: 6 }}>
<Text style={styles.title}>{t('chat.create_group')}</Text>
<TextInput
value={name}
onChangeText={setName}
placeholder={t('chat.room_name')}
placeholderTextColor="#a3a3a3"
style={styles.input}
maxLength={60}
/>
<TextInput
value={description}
onChangeText={setDescription}
placeholder={t('chat.room_description')}
placeholderTextColor="#a3a3a3"
multiline
style={[styles.input, { height: 70, textAlignVertical: 'top' }]}
maxLength={250}
/>
{/* Public toggle */}
<TouchableOpacity activeOpacity={0.7} style={styles.toggleRow} onPress={() => setIsPublic((v) => !v)}>
<Text style={styles.toggleLabel}>{t('chat.public_room')}</Text>
<View style={[styles.toggle, isPublic && styles.toggleOn]}>
<View style={[styles.toggleKnob, isPublic && styles.toggleKnobOn]} />
</View>
</TouchableOpacity>
{/* Join mode (private only) */}
{!isPublic && (
<View style={{ marginTop: 8 }}>
<Text style={styles.subLabel}>{t('chat.join_mode')}</Text>
<View style={styles.modeRow}>
{(['approval', 'invite_only'] as const).map((mode) => (
<TouchableOpacity
key={mode}
activeOpacity={0.7}
style={[styles.modeBtn, joinMode === mode && styles.modeBtnActive]}
onPress={() => setJoinMode(mode)}
>
<Text
style={[
styles.modeBtnText,
joinMode === mode && styles.modeBtnTextActive,
]}
>
{t(`chat.join_mode_${mode === 'approval' ? 'approval' : 'invite'}`)}
</Text>
</TouchableOpacity>
))}
</View>
</View>
)}
<View style={{ flex: 1 }} />
{/* Actions */}
<View style={styles.actions}>
<TouchableOpacity activeOpacity={0.7} onPress={handleClose} style={styles.cancelBtn}>
<Text style={styles.cancelText}>{t('common.cancel')}</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={create}
disabled={!name.trim() || creating}
style={[styles.createBtn, { opacity: !name.trim() || creating ? 0.5 : 1 }]}
>
{creating ? (
<ActivityIndicator size="small" color="#fff" />
) : (
<Text style={styles.createText}>{t('chat.create')}</Text>
)}
</TouchableOpacity>
</View>
</View>
</KeyboardAwareSheet>
);
}
function makeStyles(colors: ReturnType<typeof useColors>) {
return StyleSheet.create({
title: {
fontSize: 17,
fontFamily: 'Nunito_700Bold',
color: colors.text,
marginBottom: 14,
},
input: {
backgroundColor: colors.surfaceElevated,
borderRadius: 12,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 14,
fontFamily: 'Nunito_400Regular',
color: colors.text,
marginBottom: 10,
},
toggleRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 6,
marginTop: 4,
},
toggleLabel: {
fontSize: 14,
fontFamily: 'Nunito_600SemiBold',
color: colors.text,
},
toggle: {
width: 46,
height: 28,
borderRadius: 14,
backgroundColor: colors.surfaceElevated,
padding: 2,
justifyContent: 'center',
},
toggleOn: {
backgroundColor: '#007AFF',
},
toggleKnob: {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: colors.bg,
shadowColor: '#000',
shadowOpacity: 0.15,
shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
elevation: 2,
},
toggleKnobOn: {
transform: [{ translateX: 18 }],
},
subLabel: {
fontSize: 12,
fontFamily: 'Nunito_600SemiBold',
color: colors.textMuted,
marginBottom: 6,
},
modeRow: {
flexDirection: 'row',
},
modeBtn: {
flex: 1,
paddingVertical: 8,
borderRadius: 10,
borderWidth: 1,
borderColor: colors.border,
alignItems: 'center',
marginRight: 6,
},
modeBtnActive: {
backgroundColor: colors.surface,
borderColor: '#007AFF',
},
modeBtnText: {
fontSize: 12,
fontFamily: 'Nunito_600SemiBold',
color: colors.textMuted,
},
modeBtnTextActive: {
color: '#007AFF',
},
actions: {
flexDirection: 'row',
marginTop: 4,
marginBottom: 10,
},
cancelBtn: {
flex: 1,
backgroundColor: colors.surfaceElevated,
paddingVertical: 12,
borderRadius: 12,
alignItems: 'center',
marginRight: 6,
},
cancelText: {
fontSize: 14,
fontFamily: 'Nunito_600SemiBold',
color: colors.text,
},
createBtn: {
flex: 1,
backgroundColor: '#007AFF',
paddingVertical: 12,
borderRadius: 12,
alignItems: 'center',
marginLeft: 6,
},
createText: {
fontSize: 14,
fontFamily: 'Nunito_700Bold',
color: '#fff',
},
});
}