Spaces:
Running
Running
| import { Check, Loader2, Monitor } from 'lucide-react'; | |
| import { useState } from 'react'; | |
| import { apiFetch, requestPersistentStorage, setToken } from './api.js'; | |
| export function PairingScreen({ onPaired }) { | |
| const [code, setCode] = useState(''); | |
| const [error, setError] = useState(''); | |
| const [pairing, setPairing] = useState(false); | |
| async function handlePair(event) { | |
| event.preventDefault(); | |
| setPairing(true); | |
| setError(''); | |
| try { | |
| const result = await apiFetch('/api/pair', { | |
| method: 'POST', | |
| body: { | |
| code, | |
| deviceName: navigator.platform || 'iPhone' | |
| } | |
| }); | |
| setToken(result.token); | |
| requestPersistentStorage(); | |
| onPaired(); | |
| } catch (err) { | |
| setError(err.message); | |
| } finally { | |
| setPairing(false); | |
| } | |
| } | |
| return ( | |
| <main className="pairing-screen"> | |
| <div className="pairing-mark"> | |
| <Monitor size={30} /> | |
| </div> | |
| <h1>CodexMobile</h1> | |
| <p>输入电脑端启动日志里的配对码。</p> | |
| <form className="pairing-form" onSubmit={handlePair}> | |
| <label className="sr-only" htmlFor="pairing-code">配对码</label> | |
| <input | |
| id="pairing-code" | |
| name="pairingCode" | |
| inputMode="numeric" | |
| autoComplete="one-time-code" | |
| maxLength={6} | |
| placeholder="6 位配对码" | |
| value={code} | |
| onChange={(event) => setCode(event.target.value.replace(/\D/g, '').slice(0, 6))} | |
| /> | |
| <button type="submit" disabled={code.length !== 6 || pairing}> | |
| {pairing ? <Loader2 className="spin" size={18} /> : <Check size={18} />} | |
| 连接 | |
| </button> | |
| </form> | |
| {error ? <div className="pairing-error" role="alert">{error}</div> : null} | |
| </main> | |
| ); | |
| } | |