haixunMaster/haixun-backend/web/src/components/AccountConnectionMode.tsx

131 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useState } from 'react'
import { api, ApiError } from '../api/client'
import {
connectionModeDescription,
connectionModeLabel,
connectionReadyLabel,
} from '../lib/connectionMode'
import type { ThreadsAccountConnectionData } from '../types/api'
import { useOnboarding } from '../onboarding/OnboardingContext'
import { AcLink, Badge, ChoiceCard, ErrorText, SectionTitle, SuccessText } from './ui'
type AccountConnectionModeProps = {
accountId: string
connectionsPath: string
}
export function AccountConnectionMode({ accountId, connectionsPath }: AccountConnectionModeProps) {
const { refresh: refreshOnboarding } = useOnboarding()
const [connection, setConnection] = useState<ThreadsAccountConnectionData | null>(null)
const [loading, setLoading] = useState(true)
const [busy, setBusy] = useState(false)
const [error, setError] = useState('')
const [message, setMessage] = useState('')
const load = async () => {
if (!accountId) return
setLoading(true)
setError('')
try {
const data = await api.get<ThreadsAccountConnectionData>(
`/api/v1/threads-accounts/${encodeURIComponent(accountId)}/connection`,
{ auth: true },
)
setConnection(data)
} catch (e) {
setError(e instanceof ApiError ? e.message : '載入連線設定失敗')
setConnection(null)
} finally {
setLoading(false)
}
}
useEffect(() => {
load().catch(() => undefined)
}, [accountId])
const saveDevMode = async (devMode: boolean) => {
if (!accountId) return
setBusy(true)
setError('')
setMessage('')
try {
const data = await api.patch<ThreadsAccountConnectionData>(
`/api/v1/threads-accounts/${encodeURIComponent(accountId)}/connection`,
{ dev_mode: devMode },
{ auth: true },
)
setConnection(data)
setMessage(devMode ? '已切換為開發模式(爬蟲)' : '已切換為正式模式API')
await refreshOnboarding()
} catch (e) {
setError(e instanceof ApiError ? e.message : '儲存失敗')
} finally {
setBusy(false)
}
}
const prefs = connection?.prefs
const devMode = !!prefs?.dev_mode
return (
<div className="grid gap-4">
<div className="flex flex-wrap items-start justify-between gap-3">
<div className="grid gap-2">
<SectionTitle></SectionTitle>
<p className="text-sm leading-relaxed text-ink-secondary">
Threads APIOAuthChrome {' '}
<AcLink to={connectionsPath}></AcLink>
</p>
</div>
{connection ? (
<div className="flex flex-wrap gap-2">
<Badge tone={devMode ? 'warning' : 'brand'}>{connectionModeLabel(devMode)}</Badge>
<Badge
tone={
devMode
? connection.browser_connected
? 'success'
: 'warning'
: connection.api_connected
? 'success'
: 'warning'
}
>
{connectionReadyLabel(devMode, connection.browser_connected, connection.api_connected)}
</Badge>
</div>
) : null}
</div>
{loading ? (
<p className="text-sm text-muted"></p>
) : prefs ? (
<>
<p className="text-sm text-ink-secondary">{connectionModeDescription(devMode)}</p>
<div className="grid gap-3 sm:grid-cols-2">
<ChoiceCard
title="正式模式API"
hint="搜尋、發文、留言走官方 API"
active={!devMode}
disabled={busy || !devMode}
onClick={() => saveDevMode(false)}
/>
<ChoiceCard
title="開發模式(爬蟲)"
hint="本機除錯用,需同步 Chrome Session"
active={devMode}
disabled={busy || devMode}
onClick={() => saveDevMode(true)}
/>
</div>
</>
) : (
<p className="text-sm text-ink-secondary"></p>
)}
<ErrorText message={error} />
<SuccessText message={message} />
</div>
)
}