Files
quibot/apk/app/settings.tsx
2026-06-18 13:45:32 +02:00

253 lines
6.4 KiB
TypeScript

import { Picker } from "@react-native-picker/picker";
import { router } from "expo-router";
import { useEffect, useState } from "react";
import {
Alert,
KeyboardAvoidingView,
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import {
loadRecorderSettings,
saveRecorderSettings,
} from "@/lib/recorder-settings";
import { AVAILABLE_LOCALES, t, type Locale, getStrings } from "@/lib/translations";
function localeLabel(locale: Locale) {
if (locale === "ca") return "Catal\u00e0";
if (locale === "en") return "English";
return locale;
}
export default function SettingsScreen() {
const [backendUrl, setBackendUrl] = useState("");
const [authToken, setAuthToken] = useState("");
const [fieldName, setFieldName] = useState("file");
const [language, setLanguage] = useState<Locale>("ca");
const [strings, setStrings] = useState(() => getStrings("ca"));
useEffect(() => {
let isMounted = true;
async function loadSettings() {
try {
const settings = await loadRecorderSettings();
if (!isMounted) {
return;
}
setBackendUrl(settings.backendUrl);
setAuthToken(settings.authToken);
setFieldName(settings.fieldName);
setLanguage(settings.language);
setStrings(getStrings(settings.language));
} catch {
if (isMounted) {
Alert.alert(strings.loadError, strings.loadError);
}
}
}
void loadSettings();
return () => {
isMounted = false;
};
}, []);
const langStrings = getStrings(language);
async function handleSave() {
try {
await saveRecorderSettings({
authToken,
backendUrl,
fieldName,
language,
});
setStrings(getStrings(language));
router.back();
} catch {
Alert.alert(langStrings.saveError, langStrings.saveError);
}
}
function handleLanguageChange(val: Locale) {
setLanguage(val);
setStrings(getStrings(val));
}
return (
<SafeAreaView style={styles.safeArea}>
<KeyboardAvoidingView
style={styles.keyboardAvoidingView}
behavior={Platform.OS === "ios" ? "padding" : undefined}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.content}
keyboardShouldPersistTaps="handled"
>
<View style={styles.headerRow}>
<Pressable onPress={() => router.back()} style={styles.navButton}>
<Text style={styles.navButtonText}>{langStrings.back}</Text>
</Pressable>
<Text style={styles.title}>{langStrings.settingsTitle}</Text>
<Pressable onPress={handleSave} style={styles.navButton}>
<Text style={styles.navButtonText}>{langStrings.save}</Text>
</Pressable>
</View>
<View style={styles.panel}>
<Text style={styles.label}>{langStrings.backendUrl}</Text>
<TextInput
autoCapitalize="none"
autoCorrect={false}
keyboardType="url"
onChangeText={setBackendUrl}
placeholder={langStrings.urlPlaceholder}
placeholderTextColor="#8f8a7c"
style={styles.input}
value={backendUrl}
/>
<Text style={styles.label}>{langStrings.bearerToken}</Text>
<TextInput
autoCapitalize="none"
autoCorrect={false}
onChangeText={setAuthToken}
placeholder={langStrings.tokenOptional}
placeholderTextColor="#8f8a7c"
secureTextEntry
style={styles.input}
value={authToken}
/>
<Text style={styles.label}>{langStrings.formFieldName}</Text>
<TextInput
autoCapitalize="none"
autoCorrect={false}
onChangeText={setFieldName}
placeholder={langStrings.fieldNamePlaceholder}
placeholderTextColor="#8f8a7c"
style={styles.input}
value={fieldName}
/>
<Text style={styles.helperText}>
{t("helperText", language, fieldName.trim() || "file")}
</Text>
</View>
<View style={styles.panel}>
<Text style={styles.label}>{langStrings.languageTitle}</Text>
<View style={styles.pickerWrapper}>
<Picker
selectedValue={language}
onValueChange={handleLanguageChange}
style={styles.picker}
>
{AVAILABLE_LOCALES.map((loc) => (
<Picker.Item
key={loc}
label={localeLabel(loc)}
value={loc}
/>
))}
</Picker>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: "#f4efe4",
},
keyboardAvoidingView: {
flex: 1,
},
scrollView: {
flex: 1,
},
content: {
gap: 18,
paddingHorizontal: 20,
paddingBottom: 32,
paddingTop: 8,
},
headerRow: {
alignItems: "center",
flexDirection: "row",
justifyContent: "space-between",
},
navButton: {
borderColor: "#cdbfa8",
borderRadius: 999,
borderWidth: 1,
paddingHorizontal: 12,
paddingVertical: 8,
},
navButtonText: {
color: "#13304a",
fontSize: 14,
fontWeight: "700",
},
title: {
color: "#13304a",
fontSize: 28,
fontWeight: "800",
},
panel: {
backgroundColor: "#fffaf1",
borderColor: "#dccfb9",
borderRadius: 24,
borderWidth: 1,
gap: 12,
padding: 18,
},
label: {
color: "#13304a",
fontSize: 13,
fontWeight: "700",
marginBottom: -4,
textTransform: "uppercase",
},
input: {
backgroundColor: "#f7f0e0",
borderColor: "#d9ccb5",
borderRadius: 16,
borderWidth: 1,
color: "#1a2b39",
fontSize: 16,
paddingHorizontal: 14,
paddingVertical: 14,
},
helperText: {
color: "#665f54",
fontSize: 13,
lineHeight: 18,
},
pickerWrapper: {
backgroundColor: "#f7f0e0",
borderColor: "#d9ccb5",
borderRadius: 16,
borderWidth: 1,
overflow: "hidden",
},
picker: {
height: 50,
},
});