import { useState } from 'react' import { AlertTriangle, Check, Loader2, ShieldAlert } from 'lucide-react' import Modal from '../../shared/Modal' import ToggleSwitch from '../../Settings/ToggleSwitch' import { useTranslation } from '../../../i18n/TranslationContext' import { useConnectForm } from './useConnectForm' import { Badge } from './DocSyncBits' import { DOCUMENT_PROVIDER_ICONS } from '../../shared/DocumentProviderIcons' import type { DocSyncProvider, useDocSync } from './useDocSync' /** * Connecting a store, as a dialog rather than a panel that unfolds in place. * * Entering a URL and a token is a task with a beginning and an end, and doing it * inside the list meant the list jumped around while someone typed. The dialog * also gives the probe result somewhere to live that is not a line of text * squeezed between two fields. */ export default function DocSyncConnectModal({ provider, sync, onClose, onConnected, }: { provider: DocSyncProvider sync: ReturnType onClose: () => void onConnected: (providerId: string) => void }) { const { t } = useTranslation() const existing = sync.connectionFor(provider.id) const Icon = DOCUMENT_PROVIDER_ICONS[provider.id] const form = useConnectForm(provider, existing, sync) const [verdict, setVerdict] = useState<{ connected: boolean; account?: string; error?: string } | null>(null) const test = async () => setVerdict(await form.probe()) const save = async () => { if (await form.save()) onConnected(provider.id) } return ( {Icon && } {provider.name} } footer={
} >

{t(`docsync.connect.about.${provider.id}`)}

{form.fields.map(f => ( form.setValue(f.field_key, e.target.value)} className="w-full rounded-lg border border-edge bg-surface-input px-3 py-2.5 text-body text-content ring-accent transition-shadow placeholder:text-content-faint focus:outline-none focus:ring-2" /> ))}
) } function Field({ label, hint, required, children, }: { label: string hint?: string required?: boolean children: React.ReactNode }) { const { t } = useTranslation() return ( ) } /** The probe result, in the footer where it stays put while the form scrolls. */ function Verdict({ verdict, busy, failure, }: { verdict: { connected: boolean; account?: string; error?: string } | null busy: boolean /** A call that failed outside the connection test (saving, most of all). */ failure: string | null }) { const { t } = useTranslation() if (busy) { return ( {t('docsync.connect.testing')} ) } // A save that the server refused left the dialog open with the button back to // normal and nothing else said, which reads as nothing having happened. The // test verdict wins the space when there is one: it is the more specific // answer and the one the person just asked for. if (!verdict && failure) { return ( {t(`docsync.error.${failure}`)} ) } if (!verdict) return if (verdict.connected) { return ( {verdict.account ? t('docsync.connect.okAs', { account: verdict.account }) : t('docsync.connected')} ) } const blocked = verdict.error === 'ssrf_blocked' || verdict.error === 'tls_untrusted' return ( {blocked ? : } {t(`docsync.error.${verdict.error || 'unknown'}`)} ) }