React Native Expo app — full mobile client
- Expo SDK 54, expo-router, NativeWind - 7 screens: login, tasks, calendar, goals, chat, settings, tabs - API client (api.hasdo.info), SecureStore auth, AuthContext - Tab navigation: Ukoly, Kalendar, Cile, Chat, Nastaveni - Pull-to-refresh, FAB, group colors, priority dots - Calendar grid with task dots - AI chat with keyboard avoiding - Web export verified (780 modules, 1.5MB bundle) - Bundle ID: info.hasdo.taskteam Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
220
mobile/app/(tabs)/settings.tsx
Normal file
220
mobile/app/(tabs)/settings.tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Switch,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useAuthContext } from '../../lib/AuthContext';
|
||||
import Constants from 'expo-constants';
|
||||
|
||||
export default function SettingsScreen() {
|
||||
const { user, logout } = useAuthContext();
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
const [notifications, setNotifications] = useState(true);
|
||||
const [language, setLanguage] = useState<'cs' | 'en'>('cs');
|
||||
|
||||
const handleLogout = () => {
|
||||
Alert.alert(
|
||||
'Odhlaseni',
|
||||
'Opravdu se chcete odhlasit?',
|
||||
[
|
||||
{ text: 'Zrusit', style: 'cancel' },
|
||||
{
|
||||
text: 'Odhlasit',
|
||||
style: 'destructive',
|
||||
onPress: logout,
|
||||
},
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
const renderSection = (title: string, children: React.ReactNode) => (
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>{title}</Text>
|
||||
<View style={styles.sectionContent}>{children}</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
const renderRow = (
|
||||
icon: string,
|
||||
label: string,
|
||||
right?: React.ReactNode,
|
||||
onPress?: () => void,
|
||||
) => (
|
||||
<TouchableOpacity
|
||||
style={styles.row}
|
||||
onPress={onPress}
|
||||
disabled={!onPress && !right}
|
||||
activeOpacity={onPress ? 0.7 : 1}
|
||||
>
|
||||
<View style={styles.rowLeft}>
|
||||
<Ionicons name={icon as any} size={20} color="#64748B" />
|
||||
<Text style={styles.rowLabel}>{label}</Text>
|
||||
</View>
|
||||
{right || (onPress && <Ionicons name="chevron-forward" size={18} color="#CBD5E1" />)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.container}>
|
||||
{/* User card */}
|
||||
<View style={styles.userCard}>
|
||||
<View style={styles.userAvatar}>
|
||||
<Text style={styles.userAvatarText}>
|
||||
{(user?.name || 'U')[0].toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.userInfo}>
|
||||
<Text style={styles.userName}>{user?.name || 'Uzivatel'}</Text>
|
||||
<Text style={styles.userEmail}>{user?.email || ''}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{renderSection('Predvolby', (
|
||||
<>
|
||||
{renderRow(
|
||||
'moon-outline',
|
||||
'Tmavy rezim',
|
||||
<Switch
|
||||
value={darkMode}
|
||||
onValueChange={setDarkMode}
|
||||
trackColor={{ false: '#E2E8F0', true: '#3B82F6' }}
|
||||
/>,
|
||||
)}
|
||||
{renderRow(
|
||||
'notifications-outline',
|
||||
'Oznameni',
|
||||
<Switch
|
||||
value={notifications}
|
||||
onValueChange={setNotifications}
|
||||
trackColor={{ false: '#E2E8F0', true: '#3B82F6' }}
|
||||
/>,
|
||||
)}
|
||||
{renderRow(
|
||||
'language-outline',
|
||||
'Jazyk',
|
||||
<TouchableOpacity
|
||||
style={styles.langToggle}
|
||||
onPress={() => setLanguage(language === 'cs' ? 'en' : 'cs')}
|
||||
>
|
||||
<Text style={styles.langText}>
|
||||
{language === 'cs' ? 'Cestina' : 'English'}
|
||||
</Text>
|
||||
</TouchableOpacity>,
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
|
||||
{renderSection('Ucet', (
|
||||
<>
|
||||
{renderRow('person-outline', 'Upravit profil', undefined, () =>
|
||||
Alert.alert('Info', 'Bude k dispozici brzy'),
|
||||
)}
|
||||
{renderRow('lock-closed-outline', 'Zmenit heslo', undefined, () =>
|
||||
Alert.alert('Info', 'Bude k dispozici brzy'),
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
|
||||
{renderSection('Informace', (
|
||||
<>
|
||||
{renderRow('information-circle-outline', 'Verze', (
|
||||
<Text style={styles.versionText}>
|
||||
{Constants.expoConfig?.version || '1.0.0'}
|
||||
</Text>
|
||||
))}
|
||||
{renderRow('document-text-outline', 'Podminky pouziti', undefined, () =>
|
||||
Alert.alert('Info', 'Bude k dispozici brzy'),
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
|
||||
{/* Logout */}
|
||||
<TouchableOpacity style={styles.logoutBtn} onPress={handleLogout}>
|
||||
<Ionicons name="log-out-outline" size={20} color="#EF4444" />
|
||||
<Text style={styles.logoutText}>Odhlasit se</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={{ height: 40 }} />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, backgroundColor: '#F8FAFC' },
|
||||
userCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#fff',
|
||||
padding: 20,
|
||||
margin: 12,
|
||||
borderRadius: 12,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 3,
|
||||
elevation: 2,
|
||||
},
|
||||
userAvatar: {
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 28,
|
||||
backgroundColor: '#3B82F6',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
userAvatarText: { color: '#fff', fontSize: 22, fontWeight: '700' },
|
||||
userInfo: { marginLeft: 16, flex: 1 },
|
||||
userName: { fontSize: 18, fontWeight: '600', color: '#1E293B' },
|
||||
userEmail: { fontSize: 14, color: '#64748B', marginTop: 2 },
|
||||
section: { marginTop: 12, marginHorizontal: 12 },
|
||||
sectionTitle: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
color: '#64748B',
|
||||
textTransform: 'uppercase',
|
||||
marginBottom: 6,
|
||||
marginLeft: 4,
|
||||
},
|
||||
sectionContent: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 14,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#F1F5F9',
|
||||
},
|
||||
rowLeft: { flexDirection: 'row', alignItems: 'center', gap: 12 },
|
||||
rowLabel: { fontSize: 15, color: '#1E293B' },
|
||||
langToggle: {
|
||||
backgroundColor: '#F1F5F9',
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 8,
|
||||
},
|
||||
langText: { fontSize: 13, color: '#3B82F6', fontWeight: '600' },
|
||||
versionText: { fontSize: 14, color: '#94A3B8' },
|
||||
logoutBtn: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
marginTop: 24,
|
||||
marginHorizontal: 12,
|
||||
paddingVertical: 14,
|
||||
backgroundColor: '#FEF2F2',
|
||||
borderRadius: 12,
|
||||
},
|
||||
logoutText: { color: '#EF4444', fontSize: 16, fontWeight: '600' },
|
||||
});
|
||||
Reference in New Issue
Block a user