Fixes
This commit is contained in:
223
mobile/app/more/settings.tsx
Normal file
223
mobile/app/more/settings.tsx
Normal file
@@ -0,0 +1,223 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
|
||||
import { router } from 'expo-router';
|
||||
|
||||
import { DEFAULT_API_BASE_URL } from '@/src/config/env';
|
||||
import {
|
||||
getApiBaseUrlSync,
|
||||
hydrateApiBaseUrl,
|
||||
resetApiBaseUrl,
|
||||
serverStorageInfo,
|
||||
setApiBaseUrl,
|
||||
} from '@/src/lib/server-config';
|
||||
import { useAuth } from '@/src/providers/auth-provider';
|
||||
|
||||
const PRIMARY = '#69c350';
|
||||
|
||||
function isValidServerUrl(value: string): boolean {
|
||||
return /^https?:\/\/.+/i.test(value.trim());
|
||||
}
|
||||
|
||||
export default function SettingsScreen() {
|
||||
const { logout, token } = useAuth();
|
||||
const [serverUrl, setServerUrlInput] = useState(getApiBaseUrlSync());
|
||||
const [savedUrl, setSavedUrl] = useState(getApiBaseUrlSync());
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
|
||||
const loadConfig = useCallback(async () => {
|
||||
const current = await hydrateApiBaseUrl();
|
||||
setServerUrlInput(current);
|
||||
setSavedUrl(current);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void loadConfig();
|
||||
}, [loadConfig]);
|
||||
|
||||
async function onSave() {
|
||||
setError(null);
|
||||
setSuccess(null);
|
||||
|
||||
if (!isValidServerUrl(serverUrl)) {
|
||||
setError('Bitte eine gültige URL mit http:// oder https:// eingeben.');
|
||||
return;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const normalized = await setApiBaseUrl(serverUrl);
|
||||
setServerUrlInput(normalized);
|
||||
setSavedUrl(normalized);
|
||||
setSuccess('Server-Instanz gespeichert.');
|
||||
|
||||
if (token) {
|
||||
await logout();
|
||||
router.replace('/login');
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Server-Instanz konnte nicht gespeichert werden.');
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function onResetToDefault() {
|
||||
setError(null);
|
||||
setSuccess(null);
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
const fallback = await resetApiBaseUrl();
|
||||
setServerUrlInput(fallback);
|
||||
setSavedUrl(fallback);
|
||||
setSuccess('Auf Standard-Server zurückgesetzt.');
|
||||
|
||||
if (token) {
|
||||
await logout();
|
||||
router.replace('/login');
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Zuruecksetzen fehlgeschlagen.');
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
const isDirty = serverUrl.trim() !== savedUrl;
|
||||
|
||||
return (
|
||||
<ScrollView contentContainerStyle={styles.container}>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.title}>Server-Instanz</Text>
|
||||
<Text style={styles.hint}>
|
||||
Hinterlege hier die URL deiner eigenen FEDEO-Server-Instanz. Nach dem Speichern wird die Session
|
||||
neu gestartet.
|
||||
</Text>
|
||||
|
||||
<Text style={styles.label}>Server URL</Text>
|
||||
<TextInput
|
||||
value={serverUrl}
|
||||
onChangeText={setServerUrlInput}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
keyboardType="url"
|
||||
placeholder="https://dein-server.tld"
|
||||
placeholderTextColor="#9ca3af"
|
||||
style={styles.input}
|
||||
/>
|
||||
|
||||
<Text style={styles.meta}>Aktiv: {savedUrl}</Text>
|
||||
<Text style={styles.meta}>Standard: {DEFAULT_API_BASE_URL}</Text>
|
||||
<Text style={styles.meta}>Storage: {serverStorageInfo.mode}</Text>
|
||||
|
||||
{error ? <Text style={styles.error}>{error}</Text> : null}
|
||||
{success ? <Text style={styles.success}>{success}</Text> : null}
|
||||
|
||||
<View style={styles.actions}>
|
||||
<Pressable
|
||||
style={[styles.saveButton, (!isDirty || submitting) ? styles.buttonDisabled : null]}
|
||||
onPress={onSave}
|
||||
disabled={!isDirty || submitting}>
|
||||
<Text style={styles.saveButtonText}>{submitting ? 'Speichern...' : 'Speichern'}</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
style={[styles.resetButton, submitting ? styles.buttonDisabled : null]}
|
||||
onPress={onResetToDefault}
|
||||
disabled={submitting}>
|
||||
<Text style={styles.resetButtonText}>Auf Standard zurücksetzen</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexGrow: 1,
|
||||
backgroundColor: '#f9fafb',
|
||||
padding: 16,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#ffffff',
|
||||
borderWidth: 1,
|
||||
borderColor: '#e5e7eb',
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
gap: 10,
|
||||
},
|
||||
title: {
|
||||
color: '#111827',
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
},
|
||||
hint: {
|
||||
color: '#6b7280',
|
||||
fontSize: 13,
|
||||
lineHeight: 18,
|
||||
},
|
||||
label: {
|
||||
color: '#374151',
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
marginTop: 2,
|
||||
},
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#d1d5db',
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
fontSize: 15,
|
||||
color: '#111827',
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
meta: {
|
||||
color: '#6b7280',
|
||||
fontSize: 12,
|
||||
},
|
||||
actions: {
|
||||
gap: 8,
|
||||
marginTop: 6,
|
||||
},
|
||||
saveButton: {
|
||||
minHeight: 42,
|
||||
backgroundColor: PRIMARY,
|
||||
borderRadius: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
saveButtonText: {
|
||||
color: '#ffffff',
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
},
|
||||
resetButton: {
|
||||
minHeight: 42,
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: '#d1d5db',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
resetButtonText: {
|
||||
color: '#374151',
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
},
|
||||
buttonDisabled: {
|
||||
opacity: 0.6,
|
||||
},
|
||||
error: {
|
||||
color: '#dc2626',
|
||||
fontSize: 13,
|
||||
},
|
||||
success: {
|
||||
color: '#166534',
|
||||
fontSize: 13,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user