feat(desktop): wrap the web client in a Tauri app
ci / api / api (push) Successful in 43s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m19s
ci / desktop / rust (desktop proxy) (push) Failing after 3m34s
ci / ts / ts (packages + web) (push) Failing after 2m49s
release / release (версия и заметки) (push) Successful in 5s
ci / db / db (push) Successful in 40s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m12s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m28s
release / android (APK в релиз) (push) Failing after 2m0s
ci / api / api (push) Successful in 43s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m19s
ci / desktop / rust (desktop proxy) (push) Failing after 3m34s
ci / ts / ts (packages + web) (push) Failing after 2m49s
release / release (версия и заметки) (push) Successful in 5s
ci / db / db (push) Successful in 40s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m12s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m28s
release / android (APK в релиз) (push) Failing after 2m0s
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fa79394869
commit
efe78d7e07
33 files changed
+1056
-20
No files matched your search
@@ -0,0 +1,55 @@
|
||||
import { useState, type FormEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from '@tanstack/react-router';
|
||||
|
||||
import { useDesktop } from '../desktop';
|
||||
import { Button, Field, FormError } from '../shared/ui/ui';
|
||||
import { AuthLayout } from './AuthLayout';
|
||||
import s from './auth.module.css';
|
||||
|
||||
export function ConnectPage() {
|
||||
const { t } = useTranslation();
|
||||
const desktop = useDesktop();
|
||||
const router = useRouter();
|
||||
const [address, setAddress] = useState(desktop?.server ?? '');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const submit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!desktop) return;
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await desktop.setServer(address);
|
||||
await router.invalidate();
|
||||
await router.navigate({ to: '/login' });
|
||||
} catch (err) {
|
||||
const text = err instanceof Error ? err.message : '';
|
||||
setError(/Invalid|query|fragment|credentials/i.test(text) ? t('server.invalid_address') : t('server.offline'));
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthLayout>
|
||||
<h1>{t('server.title')}</h1>
|
||||
<p className={s.lead}>{t('app.tagline')}</p>
|
||||
<form onSubmit={(e) => void submit(e)} noValidate>
|
||||
<Field
|
||||
label={t('server.address')}
|
||||
value={address}
|
||||
onChange={setAddress}
|
||||
autoComplete="url"
|
||||
autoCapitalize="none"
|
||||
spellCheck={false}
|
||||
required
|
||||
/>
|
||||
<FormError>{error}</FormError>
|
||||
<Button type="submit" variant="primary" className={s.submit} busy={busy} disabled={!address.trim()}>
|
||||
{t('mobile.connect')}
|
||||
</Button>
|
||||
</form>
|
||||
</AuthLayout>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user