14c880123c
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
2.0 KiB
TypeScript
78 lines
2.0 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { StyleSheet, Text, View } from "react-native";
|
|
|
|
import { Input } from "@/components/ui/Input";
|
|
import { fonts, spacing } from "@/constants/theme";
|
|
import { useAccounts } from "@/contexts/AccountsContext";
|
|
import { useAppTheme } from "@/contexts/ThemeContext";
|
|
import { normalizeInstanceUrl } from "@/lib/instance-url";
|
|
|
|
type InstanceUrlFieldProps = {
|
|
onSaved?: (url: string) => void;
|
|
};
|
|
|
|
export function InstanceUrlField({ onSaved }: InstanceUrlFieldProps) {
|
|
const { colors } = useAppTheme();
|
|
const { apiUrl, setInstanceUrl } = useAccounts();
|
|
const [value, setValue] = useState(apiUrl);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
setValue(apiUrl);
|
|
}, [apiUrl]);
|
|
|
|
async function commit() {
|
|
const trimmed = value.trim();
|
|
if (!trimmed || trimmed === apiUrl) {
|
|
setError(null);
|
|
return;
|
|
}
|
|
|
|
const normalized = normalizeInstanceUrl(trimmed);
|
|
if (!normalized) {
|
|
setError("Enter a valid URL like beenvoice.app or localhost:3000");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const saved = await setInstanceUrl(trimmed);
|
|
setValue(saved);
|
|
setError(null);
|
|
onSaved?.(saved);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Could not save server URL");
|
|
}
|
|
}
|
|
|
|
return (
|
|
<View style={styles.wrapper}>
|
|
<Input
|
|
label="Server instance"
|
|
value={value}
|
|
onChangeText={setValue}
|
|
onBlur={commit}
|
|
onSubmitEditing={commit}
|
|
autoCapitalize="none"
|
|
autoCorrect={false}
|
|
keyboardType="url"
|
|
placeholder="beenvoice.app or localhost:3000"
|
|
error={error ?? undefined}
|
|
/>
|
|
<Text style={[styles.hint, { color: colors.mutedForeground }]}>
|
|
Point the app at your beenvoice server. Use your Mac's LAN IP on a physical device.
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
wrapper: {
|
|
gap: spacing.xs,
|
|
},
|
|
hint: {
|
|
fontSize: 12,
|
|
fontFamily: fonts.body,
|
|
lineHeight: 16,
|
|
},
|
|
});
|