import { createApiClient } from '@pipu/api-client'; import { checkServer, normalizeServerUrl, type ServerCheck } from '@pipu/core'; import Constants from 'expo-constants'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ActivityIndicator, Pressable, StyleSheet, Text, TextInput, View } from 'react-native'; import { colors, radius } from './theme'; const appVersionCode = Number(Constants.expoConfig?.extra?.versionCode ?? 0); type State = { kind: 'idle' } | { kind: 'checking' } | { kind: 'invalid' } | ({ kind: 'done' } & ServerCheck); /** Экран подключения к серверу (docs/mobile.md). В M0 — только проверка /server-info. */ export function ConnectScreen() { const { t } = useTranslation(); const [address, setAddress] = useState(''); const [state, setState] = useState({ kind: 'idle' }); const checking = state.kind === 'checking'; async function onCheck() { if (checking) return; const baseUrl = normalizeServerUrl(address); if (!baseUrl) { setState({ kind: 'invalid' }); return; } setState({ kind: 'checking' }); const res = await checkServer(createApiClient({ baseUrl }), appVersionCode); setState({ kind: 'done', ...res }); } return ( {t('app.name')} {t('app.tagline')} {t('server.address')} {state.kind === 'checking' && ( {t('server.checking')} )} {state.kind === 'invalid' && } {state.kind === 'done' && state.status === 'ok' && ( )} {state.kind === 'done' && state.status === 'unsupported_app' && ( )} {state.kind === 'done' && state.status === 'offline' && } [styles.button, (pressed || checking) && { opacity: 0.6 }]} > {t('server.check')} ); } function Status({ ok, text }: { ok: boolean; text: string }) { return ( {text} ); } const styles = StyleSheet.create({ screen: { flex: 1, backgroundColor: colors.bg, justifyContent: 'center', padding: 16 }, card: { gap: 12, padding: 24, borderRadius: radius.xl, backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.line2, }, title: { color: colors.text, fontSize: 26, fontWeight: '600' }, muted: { color: colors.text2, lineHeight: 21 }, label: { color: colors.text2, fontSize: 13, fontWeight: '500', marginTop: 8 }, input: { height: 48, paddingHorizontal: 14, borderRadius: radius.md, backgroundColor: colors.surface2, borderWidth: 1, borderColor: colors.line2, color: colors.text, fontSize: 15, }, status: { flexDirection: 'row', alignItems: 'center', gap: 8, padding: 12, borderRadius: 10 }, button: { height: 48, borderRadius: radius.md, backgroundColor: colors.accent, alignItems: 'center', justifyContent: 'center', }, buttonText: { color: '#fff', fontWeight: '600', fontSize: 15 }, });