mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 00:25:38 -04:00
Improve server URL input UX on native app launch (#9)
This commit is contained in:
@@ -63,8 +63,6 @@ export default function ServerUrlScreen() {
|
||||
const destructiveColor = useCSSVariable("--color-destructive") as string;
|
||||
const mutedFgColor = useCSSVariable("--color-muted-foreground") as string;
|
||||
|
||||
const [isFirstLaunch] = useState(() => !hasStoredServerUrl());
|
||||
|
||||
// Icon pulse animation
|
||||
const iconOpacity = useSharedValue(1);
|
||||
const iconAnimatedStyle = useAnimatedStyle(() => ({
|
||||
@@ -94,14 +92,6 @@ export default function ServerUrlScreen() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Auto-focus on first launch
|
||||
useEffect(() => {
|
||||
if (isFirstLaunch) {
|
||||
const timer = setTimeout(() => inputRef.current?.focus(), 500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [isFirstLaunch]);
|
||||
|
||||
const handleChangeText = (text: string) => {
|
||||
setUrl(text);
|
||||
if (connection.phase === "error") {
|
||||
@@ -114,6 +104,14 @@ export default function ServerUrlScreen() {
|
||||
if (!trimmed) return;
|
||||
|
||||
const fullUrl = normalizeUrl(trimmed);
|
||||
|
||||
try {
|
||||
new URL(fullUrl);
|
||||
} catch {
|
||||
setConnection({ phase: "error", error: "invalid_url" });
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||
return;
|
||||
}
|
||||
setConnection({ phase: "connecting" });
|
||||
|
||||
const result = await validateServerUrl(fullUrl);
|
||||
@@ -133,6 +131,16 @@ export default function ServerUrlScreen() {
|
||||
|
||||
const isConnecting = connection.phase === "connecting";
|
||||
const isSuccess = connection.phase === "success";
|
||||
const trimmedUrl = url.trim().replace(/\/+$/, "");
|
||||
const isValidUrl = (() => {
|
||||
if (!trimmedUrl) return false;
|
||||
try {
|
||||
new URL(normalizeUrl(trimmedUrl));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
const isDisabled = isConnecting || isSuccess;
|
||||
|
||||
return (
|
||||
@@ -187,7 +195,11 @@ export default function ServerUrlScreen() {
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Button onPress={handleConnect} className="bg-primary">
|
||||
<Button
|
||||
onPress={handleConnect}
|
||||
disabled={!isValidUrl}
|
||||
className="bg-primary"
|
||||
>
|
||||
<ButtonLabel>Connect</ButtonLabel>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ReactNode } from "react";
|
||||
import type { StyleProp, ViewStyle } from "react-native";
|
||||
import { ScrollView } from "react-native";
|
||||
import { KeyboardAvoidingView } from "react-native-keyboard-controller";
|
||||
import Animated, { FadeIn } from "react-native-reanimated";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { SofaLogo } from "@/components/ui/sofa-logo";
|
||||
@@ -22,12 +23,13 @@ export function AuthScreen({
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView behavior="padding" style={{ flex: 1 }}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 24,
|
||||
paddingTop: insets.top,
|
||||
paddingTop: insets.top + 24,
|
||||
paddingBottom: insets.bottom,
|
||||
}}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
@@ -49,10 +51,13 @@ export function AuthScreen({
|
||||
{title}
|
||||
</Text>
|
||||
{subtitle && (
|
||||
<Text className="mt-2 text-muted-foreground text-sm">{subtitle}</Text>
|
||||
<Text className="mt-2 text-muted-foreground text-sm">
|
||||
{subtitle}
|
||||
</Text>
|
||||
)}
|
||||
</Animated.View>
|
||||
{children}
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user