First commit
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
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 },
|
||||
});
|
||||
Reference in New Issue
Block a user