fix(mobile/login): one-tap public pairing, manual URL/key behind Advanced
Pairing is automatic (App.tsx auto-connects to the public instance when no creds are stored); the login screen only appears after an explicit Sign out. It defaulted to localhost + empty key, forcing manual entry that no longer reflects how pairing works. Now it prefills the public backend + shipped key (one-tap 'Connect to public instance') and tucks the URL/API-key fields under an 'Advanced · self-hosted backend' toggle for power users. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bb5a97e288
commit
7981ba3408
1 changed files with 71 additions and 33 deletions
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { GoonClient } from '../api';
|
import { GoonClient } from '../api';
|
||||||
import { GoonMark, GoonWordmark } from '../components/GoonWordmark';
|
import { GoonMark, GoonWordmark } from '../components/GoonWordmark';
|
||||||
|
import { DEFAULT_API_KEY, DEFAULT_BACKEND_URL } from '../lib/backend';
|
||||||
import { saveCredentials } from '../storage';
|
import { saveCredentials } from '../storage';
|
||||||
import { theme } from '../theme';
|
import { theme } from '../theme';
|
||||||
|
|
||||||
|
|
@ -20,9 +21,14 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LoginScreen({ onAuthenticated }: Props) {
|
export function LoginScreen({ onAuthenticated }: Props) {
|
||||||
const [baseUrl, setBaseUrl] = useState('http://localhost:8000');
|
// Pairing jest domyślnie automatyczny (App.tsx auto-connectuje do publicznej instancji
|
||||||
const [apiKey, setApiKey] = useState('');
|
// gdy brak zapisanych creds). Ten ekran pojawia się tylko po explicit "Sign out" —
|
||||||
|
// prefillujemy publiczne defaulty, więc reconnect to JEDEN tap, bez wpisywania URL/klucza.
|
||||||
|
// Ręczne pola są pod "Advanced" dla self-hosterów.
|
||||||
|
const [baseUrl, setBaseUrl] = useState(DEFAULT_BACKEND_URL);
|
||||||
|
const [apiKey, setApiKey] = useState(DEFAULT_API_KEY);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [advanced, setAdvanced] = useState(false);
|
||||||
const [urlFocused, setUrlFocused] = useState(false);
|
const [urlFocused, setUrlFocused] = useState(false);
|
||||||
const [keyFocused, setKeyFocused] = useState(false);
|
const [keyFocused, setKeyFocused] = useState(false);
|
||||||
|
|
||||||
|
|
@ -67,10 +73,13 @@ export function LoginScreen({ onAuthenticated }: Props) {
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<Text style={styles.cardLabel}>Connect</Text>
|
<Text style={styles.cardLabel}>Connect</Text>
|
||||||
<Text style={styles.cardHint}>
|
<Text style={styles.cardHint}>
|
||||||
Pair this device with your backend. Credentials are stored encrypted in
|
{advanced
|
||||||
secure-storage on this device only.
|
? 'Point the app at your own self-hosted backend. Credentials are stored encrypted in secure-storage on this device only.'
|
||||||
|
: 'Connects to the public goon instance — no setup needed.'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{advanced && (
|
||||||
|
<>
|
||||||
<Text style={styles.label}>Backend URL</Text>
|
<Text style={styles.label}>Backend URL</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, urlFocused && styles.inputFocused]}
|
style={[styles.input, urlFocused && styles.inputFocused]}
|
||||||
|
|
@ -80,7 +89,7 @@ export function LoginScreen({ onAuthenticated }: Props) {
|
||||||
onBlur={() => setUrlFocused(false)}
|
onBlur={() => setUrlFocused(false)}
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
keyboardType="url"
|
keyboardType="url"
|
||||||
placeholder="http://localhost:8000"
|
placeholder={DEFAULT_BACKEND_URL}
|
||||||
placeholderTextColor={theme.mutedDim}
|
placeholderTextColor={theme.mutedDim}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -96,6 +105,8 @@ export function LoginScreen({ onAuthenticated }: Props) {
|
||||||
placeholder="paste your X-API-Key"
|
placeholder="paste your X-API-Key"
|
||||||
placeholderTextColor={theme.mutedDim}
|
placeholderTextColor={theme.mutedDim}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Pressable
|
<Pressable
|
||||||
style={({ pressed }) => [
|
style={({ pressed }) => [
|
||||||
|
|
@ -107,15 +118,34 @@ export function LoginScreen({ onAuthenticated }: Props) {
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
<Text style={styles.buttonText}>
|
<Text style={styles.buttonText}>
|
||||||
{loading ? 'Connecting…' : 'Connect'}
|
{loading ? 'Connecting…' : advanced ? 'Connect' : 'Connect to public instance'}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
<Pressable
|
||||||
|
onPress={() => {
|
||||||
|
if (advanced) {
|
||||||
|
// Powrót do publicznej instancji — reset pól do defaultów.
|
||||||
|
setBaseUrl(DEFAULT_BACKEND_URL);
|
||||||
|
setApiKey(DEFAULT_API_KEY);
|
||||||
|
}
|
||||||
|
setAdvanced((v) => !v);
|
||||||
|
}}
|
||||||
|
hitSlop={8}
|
||||||
|
style={styles.advancedToggle}
|
||||||
|
>
|
||||||
|
<Text style={styles.advancedToggleText}>
|
||||||
|
{advanced ? '← Use public instance' : 'Advanced · self-hosted backend'}
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{advanced && (
|
||||||
<Text style={styles.footer}>
|
<Text style={styles.footer}>
|
||||||
Need help? Check the <Text style={styles.footerEm}>X-API-Key</Text> in your
|
Need help? Check the <Text style={styles.footerEm}>X-API-Key</Text> in your
|
||||||
backend .env file.
|
backend .env file.
|
||||||
</Text>
|
</Text>
|
||||||
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
);
|
);
|
||||||
|
|
@ -231,4 +261,12 @@ const styles = StyleSheet.create({
|
||||||
lineHeight: 18,
|
lineHeight: 18,
|
||||||
},
|
},
|
||||||
footerEm: { color: theme.muted, fontWeight: '700' },
|
footerEm: { color: theme.muted, fontWeight: '700' },
|
||||||
|
|
||||||
|
advancedToggle: { marginTop: 16, alignItems: 'center' },
|
||||||
|
advancedToggleText: {
|
||||||
|
color: theme.muted,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: '600',
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue