283 lines
7.1 KiB
TypeScript
283 lines
7.1 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
Modal,
|
|
View,
|
|
Text,
|
|
TextInput,
|
|
Pressable,
|
|
StyleSheet,
|
|
ActivityIndicator,
|
|
Platform,
|
|
} from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { apiFetch } from '../../lib/api';
|
|
|
|
type Props = {
|
|
visible: boolean;
|
|
onClose: () => void;
|
|
onCreated: (room: any) => void;
|
|
};
|
|
|
|
export function CreateRoomSheet({ visible, onClose, onCreated }: Props) {
|
|
const { t } = useTranslation();
|
|
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');
|
|
}
|
|
|
|
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 (
|
|
<Modal visible={visible} transparent animationType="slide" onRequestClose={onClose}>
|
|
<Pressable style={styles.backdrop} onPress={onClose}>
|
|
<Pressable style={styles.sheet} onPress={() => {}}>
|
|
<View style={styles.grabber} />
|
|
<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 */}
|
|
<Pressable
|
|
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>
|
|
</Pressable>
|
|
|
|
{/* 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) => (
|
|
<Pressable
|
|
key={mode}
|
|
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>
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
{/* Actions */}
|
|
<View style={styles.actions}>
|
|
<Pressable onPress={onClose} style={styles.cancelBtn}>
|
|
<Text style={styles.cancelText}>{t('common.cancel')}</Text>
|
|
</Pressable>
|
|
<Pressable
|
|
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>
|
|
)}
|
|
</Pressable>
|
|
</View>
|
|
</Pressable>
|
|
</Pressable>
|
|
</Modal>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
backdrop: {
|
|
flex: 1,
|
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
justifyContent: 'flex-end',
|
|
},
|
|
sheet: {
|
|
backgroundColor: '#fff',
|
|
borderTopLeftRadius: 22,
|
|
borderTopRightRadius: 22,
|
|
padding: 18,
|
|
paddingBottom: Platform.OS === 'ios' ? 32 : 18,
|
|
},
|
|
grabber: {
|
|
width: 36,
|
|
height: 4,
|
|
borderRadius: 2,
|
|
backgroundColor: '#d4d4d4',
|
|
alignSelf: 'center',
|
|
marginBottom: 12,
|
|
},
|
|
title: {
|
|
fontSize: 17,
|
|
fontFamily: 'Nunito_700Bold',
|
|
color: '#171717',
|
|
marginBottom: 14,
|
|
},
|
|
input: {
|
|
backgroundColor: '#f5f5f5',
|
|
borderRadius: 12,
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 12,
|
|
fontSize: 14,
|
|
fontFamily: 'Nunito_400Regular',
|
|
color: '#171717',
|
|
marginBottom: 10,
|
|
},
|
|
toggleRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingVertical: 6,
|
|
marginTop: 4,
|
|
},
|
|
toggleLabel: {
|
|
fontSize: 14,
|
|
fontFamily: 'Nunito_600SemiBold',
|
|
color: '#171717',
|
|
},
|
|
toggle: {
|
|
width: 46,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
backgroundColor: '#e5e5e5',
|
|
padding: 2,
|
|
justifyContent: 'center',
|
|
},
|
|
toggleOn: {
|
|
backgroundColor: '#007AFF',
|
|
},
|
|
toggleKnob: {
|
|
width: 24,
|
|
height: 24,
|
|
borderRadius: 12,
|
|
backgroundColor: '#fff',
|
|
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: '#737373',
|
|
marginBottom: 6,
|
|
},
|
|
modeRow: {
|
|
flexDirection: 'row',
|
|
},
|
|
modeBtn: {
|
|
flex: 1,
|
|
paddingVertical: 8,
|
|
borderRadius: 10,
|
|
borderWidth: 1,
|
|
borderColor: '#e5e5e5',
|
|
alignItems: 'center',
|
|
marginRight: 6,
|
|
},
|
|
modeBtnActive: {
|
|
backgroundColor: '#eff6ff',
|
|
borderColor: '#007AFF',
|
|
},
|
|
modeBtnText: {
|
|
fontSize: 12,
|
|
fontFamily: 'Nunito_600SemiBold',
|
|
color: '#737373',
|
|
},
|
|
modeBtnTextActive: {
|
|
color: '#007AFF',
|
|
},
|
|
actions: {
|
|
flexDirection: 'row',
|
|
marginTop: 20,
|
|
},
|
|
cancelBtn: {
|
|
flex: 1,
|
|
backgroundColor: '#f5f5f5',
|
|
paddingVertical: 12,
|
|
borderRadius: 12,
|
|
alignItems: 'center',
|
|
marginRight: 6,
|
|
},
|
|
cancelText: {
|
|
fontSize: 14,
|
|
fontFamily: 'Nunito_600SemiBold',
|
|
color: '#171717',
|
|
},
|
|
createBtn: {
|
|
flex: 1,
|
|
backgroundColor: '#007AFF',
|
|
paddingVertical: 12,
|
|
borderRadius: 12,
|
|
alignItems: 'center',
|
|
marginLeft: 6,
|
|
},
|
|
createText: {
|
|
fontSize: 14,
|
|
fontFamily: 'Nunito_700Bold',
|
|
color: '#fff',
|
|
},
|
|
});
|