253 lines
6.4 KiB
TypeScript
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,
|
|
},
|
|
});
|