Files
openchamber/packages/ui/src/components/onboarding/RecoveryScreen.tsx
T

134 lines
4.3 KiB
TypeScript
Raw Normal View History

import React from 'react';
import { isDesktopShell, restartDesktopApp } from '@/lib/desktop';
import { DesktopConnectionRecovery, type RecoveryVariant } from './DesktopConnectionRecovery';
import { RemoteConnectionForm } from './RemoteConnectionForm';
import { resolveRecoveryNextStep } from './desktopRecoveryRouting';
import { desktopHostsGet, desktopHostsSet } from '@/lib/desktopHosts';
import { runtimeFetch } from '@/lib/runtime-fetch';
type RecoveryScreenProps = {
/** Recovery variant */
variant: RecoveryVariant;
/** Host URL for recovery context */
hostUrl?: string;
/** Host label for recovery context */
hostLabel?: string;
/** Callback when user wants to retry */
onRetry?: () => void;
/** Callback when user chooses remote */
onChooseRemote?: () => void;
/** Whether to show the remote connection form */
showRemoteForm?: boolean;
/** Callback when closing remote form */
onCloseRemoteForm?: () => void;
/** Callback when switching to local from remote form */
onSwitchToLocalFromRemote?: () => void;
/** Callback when entering local setup */
onEnterLocalSetup?: () => void;
/** Whether retry action is in progress */
isRetrying?: boolean;
2026-07-21 21:11:13 +03:00
localAvailable?: boolean;
};
export function RecoveryScreen({
variant,
hostUrl,
hostLabel,
onRetry,
onChooseRemote,
showRemoteForm = false,
onCloseRemoteForm,
onSwitchToLocalFromRemote,
onEnterLocalSetup,
isRetrying = false,
2026-07-21 21:11:13 +03:00
localAvailable = true,
}: RecoveryScreenProps) {
// Persist the user's first choice (local or remote)
const persistFirstChoice = React.useCallback(async (choice: 'local' | 'remote') => {
if (!isDesktopShell()) return;
const config = await desktopHostsGet();
await desktopHostsSet({
...config,
// Only change defaultHostId when switching to local; remote keeps
// whatever was there (or null) until a successful connect.
...(choice === 'local' ? { defaultHostId: 'local' } : {}),
initialHostChoiceCompleted: true,
});
}, []);
const handleRecoveryRetry = React.useCallback(async () => {
// In desktop boot flow, restart the app so the native host can
// re-evaluate the boot outcome.
if (isDesktopShell()) {
await restartDesktopApp();
return;
}
await runtimeFetch('/api/config/reload', { method: 'POST' });
onRetry?.();
}, [onRetry]);
const handleRecoveryUseLocal = React.useCallback(async () => {
const step = resolveRecoveryNextStep(variant, 'use-local');
if (step.kind === 'local-setup') {
// local-unavailable + local → enter local-setup subflow without reload
onEnterLocalSetup?.();
return;
}
// switch-default-to-local → persist local choice and restart
await persistFirstChoice('local');
if (isDesktopShell()) {
await restartDesktopApp();
return;
}
window.location.reload();
}, [variant, persistFirstChoice, onEnterLocalSetup]);
const handleRecoveryUseRemote = React.useCallback(() => {
const step = resolveRecoveryNextStep(variant, 'use-remote');
if (step.kind === 'remote-form') {
onChooseRemote?.();
}
}, [variant, onChooseRemote]);
// Recovery mode — show recovery component first; only switch to remote form on explicit user action
if (showRemoteForm) {
// For remote-wrong-service, do NOT auto-populate the known bad URL
const prefillUrl = variant === 'remote-wrong-service' ? '' : (hostUrl || '');
const prefillLabel = variant === 'remote-wrong-service' ? '' : (hostLabel || '');
return (
<RemoteConnectionForm
onBack={onCloseRemoteForm || (() => onChooseRemote?.())}
initialUrl={prefillUrl}
initialLabel={prefillLabel}
isRecoveryMode={true}
2026-07-21 21:11:13 +03:00
showInstancePicker={!localAvailable}
onSwitchToLocal={localAvailable ? (onSwitchToLocalFromRemote || (() => {
persistFirstChoice('local').then(() => {
if (isDesktopShell()) {
restartDesktopApp();
} else {
onEnterLocalSetup?.();
}
});
2026-07-21 21:11:13 +03:00
})) : undefined}
/>
);
}
return (
<DesktopConnectionRecovery
variant={variant}
hostLabel={hostLabel}
hostUrl={hostUrl}
onRetry={handleRecoveryRetry}
2026-07-21 21:11:13 +03:00
onUseLocal={localAvailable ? handleRecoveryUseLocal : undefined}
onUseRemote={handleRecoveryUseRemote}
isRetrying={isRetrying}
/>
);
}