Files
HouseOrg/app/(tabs)/settings.tsx
T
2026-06-01 23:16:10 +02:00

211 lines
7.6 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
View,
Text,
TouchableOpacity,
Switch,
StyleSheet,
Alert,
Share,
ScrollView,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { MaterialIcons } from '@expo/vector-icons';
import { kv } from '../../src/lib/kv';
import { useHouseholdStore } from '../../src/hooks/useHousehold';
import { regenerateInviteToken, buildInviteLink } from '../../src/services/household';
import { updateFcmToken, requestNotificationPermission } from '../../src/services/notifications';
import { COLORS } from '../../src/constants';
import Toast from 'react-native-toast-message';
const NOTIF_KEY = 'houseorg_notifications_enabled';
export default function SettingsScreen() {
const household = useHouseholdStore((s) => s.household);
const setHousehold = useHouseholdStore((s) => s.setHousehold);
const [notificationsEnabled, setNotificationsEnabled] = useState<boolean | null>(null);
useEffect(() => {
kv.getItem(NOTIF_KEY).then((val) => {
setNotificationsEnabled(val !== 'false');
});
}, []);
const handleShareInvite = async () => {
if (!household) return;
const link = buildInviteLink(household.id, household.inviteToken);
const text = `Tritt unserem Haushalt bei: ${link}`;
if (typeof navigator !== 'undefined' && (navigator as any).share) {
await (navigator as any).share({ text, title: 'HouseOrg Einladung' });
} else if (typeof navigator !== 'undefined' && (navigator as any).clipboard) {
await (navigator as any).clipboard.writeText(text);
Toast.show({ type: 'success', text1: 'Link kopiert' });
} else {
await Share.share({ message: text, title: 'HouseOrg Einladung' });
}
};
const handleRegenerateLink = async () => {
if (!household) return;
Alert.alert(
'Link erneuern',
'Der alte Link wird ungültig. Alle Mitglieder können sich damit nicht mehr neu anmelden.',
[
{ text: 'Abbrechen', style: 'cancel' },
{
text: 'Erneuern',
style: 'destructive',
onPress: async () => {
try {
const newToken = await regenerateInviteToken(household.id);
setHousehold({ ...household, inviteToken: newToken });
Toast.show({ type: 'success', text1: 'Neuer Einladungslink erstellt' });
} catch {
Toast.show({ type: 'error', text1: 'Fehler beim Erneuern' });
}
},
},
]
);
};
const handleNotificationToggle = async (enabled: boolean) => {
if (!household) return;
if (enabled) {
const granted = await requestNotificationPermission();
if (!granted) {
Toast.show({ type: 'error', text1: 'Benachrichtigungen nicht erlaubt' });
return;
}
}
setNotificationsEnabled(enabled);
await kv.setItem(NOTIF_KEY, String(enabled));
await updateFcmToken(household.id, enabled);
};
return (
<SafeAreaView style={styles.container} edges={['top']}>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Haushalt</Text>
<View style={styles.card}>
<View style={styles.row}>
<View style={[styles.iconWrap, { backgroundColor: '#E8F5E9' }]}>
<MaterialIcons name="home" size={18} color={COLORS.primary} />
</View>
<View style={styles.rowContent}>
<Text style={styles.rowLabel}>Name</Text>
<Text style={styles.rowValue}>{household?.name}</Text>
</View>
</View>
<View style={styles.divider} />
<TouchableOpacity style={styles.row} onPress={handleShareInvite}>
<View style={[styles.iconWrap, { backgroundColor: '#E3F2FD' }]}>
<MaterialIcons name="share" size={18} color="#1E88E5" />
</View>
<View style={styles.rowContent}>
<Text style={styles.rowLabel}>Einladungslink teilen</Text>
<Text style={styles.rowHint}>Mitglieder einladen</Text>
</View>
<MaterialIcons name="chevron-right" size={20} color={COLORS.textSecondary} />
</TouchableOpacity>
<View style={styles.divider} />
<TouchableOpacity style={styles.row} onPress={handleRegenerateLink}>
<View style={[styles.iconWrap, { backgroundColor: '#FFF3E0' }]}>
<MaterialIcons name="refresh" size={18} color={COLORS.warning} />
</View>
<View style={styles.rowContent}>
<Text style={[styles.rowLabel, { color: COLORS.warning }]}>
Einladungslink erneuern
</Text>
<Text style={styles.rowHint}>Alter Link wird ungültig</Text>
</View>
</TouchableOpacity>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Benachrichtigungen</Text>
<View style={styles.card}>
<View style={styles.row}>
<View style={[styles.iconWrap, { backgroundColor: '#F3E5F5' }]}>
<MaterialIcons name="notifications" size={18} color="#8E24AA" />
</View>
<View style={styles.rowContent}>
<Text style={styles.rowLabel}>Push-Benachrichtigungen</Text>
<Text style={styles.rowHint}>Einkaufsliste & MHD-Warnungen</Text>
</View>
<Switch
value={notificationsEnabled ?? false}
disabled={notificationsEnabled === null}
onValueChange={handleNotificationToggle}
trackColor={{ false: COLORS.border, true: COLORS.primaryLight }}
thumbColor={notificationsEnabled ? COLORS.primary : COLORS.white}
/>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>App</Text>
<View style={styles.card}>
<View style={styles.row}>
<View style={[styles.iconWrap, { backgroundColor: COLORS.surface }]}>
<MaterialIcons name="info-outline" size={18} color={COLORS.textSecondary} />
</View>
<View style={styles.rowContent}>
<Text style={styles.rowLabel}>Version</Text>
<Text style={styles.rowValue}>1.0.0</Text>
</View>
</View>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: COLORS.surface },
section: { marginTop: 28, paddingHorizontal: 16 },
sectionTitle: {
fontSize: 12,
fontWeight: '600',
color: COLORS.textSecondary,
textTransform: 'uppercase',
letterSpacing: 0.6,
marginBottom: 10,
},
card: {
backgroundColor: COLORS.white,
borderRadius: 16,
overflow: 'hidden',
shadowColor: '#000',
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 1,
},
row: {
flexDirection: 'row',
alignItems: 'center',
padding: 14,
gap: 12,
},
iconWrap: {
width: 34,
height: 34,
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center',
},
rowContent: { flex: 1 },
rowLabel: { fontSize: 15, fontWeight: '500', color: COLORS.text },
rowValue: { fontSize: 13, color: COLORS.textSecondary, marginTop: 2 },
rowHint: { fontSize: 12, color: COLORS.textSecondary, marginTop: 1 },
divider: { height: StyleSheet.hairlineWidth, backgroundColor: COLORS.border, marginLeft: 60 },
});