140 lines
4.2 KiB
TypeScript
140 lines
4.2 KiB
TypeScript
import { invoke } from '@tauri-apps/api/core'
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
import { trackEvent } from '../lib/telemetry'
|
|
import { isTauri } from '../mock-tauri'
|
|
|
|
type AccountPhase = 'checking' | 'signed-out' | 'code-sent' | 'authenticated' | 'busy' | 'error'
|
|
|
|
type AccountState = {
|
|
error: string | null
|
|
expiresAt: number | null
|
|
lastCommit: string | null
|
|
phase: AccountPhase
|
|
requestId: string | null
|
|
}
|
|
|
|
type NativeAccountStatus = {
|
|
expires_at?: number
|
|
state?: 'authenticated' | 'signed_out'
|
|
}
|
|
|
|
type NativeCodeRequest = {
|
|
request_id?: string
|
|
}
|
|
|
|
type NativeKnowledgeReceipt = {
|
|
commit?: string
|
|
}
|
|
|
|
const initialState: AccountState = {
|
|
error: null,
|
|
expiresAt: null,
|
|
lastCommit: null,
|
|
phase: 'checking',
|
|
requestId: null,
|
|
}
|
|
|
|
function errorText(error: unknown): string {
|
|
if (error instanceof Error) return error.message
|
|
return String(error)
|
|
}
|
|
|
|
export function useHoloLakeAccount() {
|
|
const [state, setState] = useState<AccountState>(initialState)
|
|
|
|
const refresh = useCallback(async () => {
|
|
if (!isTauri()) {
|
|
setState((current) => ({ ...current, phase: 'signed-out' }))
|
|
return
|
|
}
|
|
try {
|
|
const status = await invoke<NativeAccountStatus>('hololake_account_status')
|
|
setState((current) => ({
|
|
...current,
|
|
error: null,
|
|
expiresAt: status.expires_at ?? null,
|
|
phase: status.state === 'authenticated' ? 'authenticated' : 'signed-out',
|
|
}))
|
|
} catch (error) {
|
|
setState((current) => ({ ...current, error: errorText(error), phase: 'signed-out' }))
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
void refresh()
|
|
}, [refresh])
|
|
|
|
const requestCode = useCallback(async (email: string) => {
|
|
setState((current) => ({ ...current, error: null, phase: 'busy' }))
|
|
trackEvent('hololake_account_code_requested', { surface: 'account-panel' })
|
|
try {
|
|
const result = await invoke<NativeCodeRequest>('hololake_account_request_email_code', { email })
|
|
if (!result.request_id) throw new Error('hololake_login_request_id_missing')
|
|
setState((current) => ({
|
|
...current,
|
|
phase: 'code-sent',
|
|
requestId: result.request_id ?? null,
|
|
}))
|
|
} catch (error) {
|
|
setState((current) => ({ ...current, error: errorText(error), phase: 'error' }))
|
|
}
|
|
}, [])
|
|
|
|
const verifyCode = useCallback(async (code: string) => {
|
|
if (!state.requestId) return
|
|
setState((current) => ({ ...current, error: null, phase: 'busy' }))
|
|
try {
|
|
const result = await invoke<NativeAccountStatus>('hololake_account_verify_email_code', {
|
|
requestId: state.requestId,
|
|
code,
|
|
})
|
|
trackEvent('hololake_account_login_completed', { result: 'authenticated' })
|
|
setState((current) => ({
|
|
...current,
|
|
expiresAt: result.expires_at ?? null,
|
|
phase: 'authenticated',
|
|
requestId: null,
|
|
}))
|
|
} catch (error) {
|
|
trackEvent('hololake_account_login_completed', { result: 'error' })
|
|
setState((current) => ({ ...current, error: errorText(error), phase: 'code-sent' }))
|
|
}
|
|
}, [state.requestId])
|
|
|
|
const syncKnowledge = useCallback(async () => {
|
|
setState((current) => ({ ...current, error: null, phase: 'busy' }))
|
|
trackEvent('hololake_knowledge_sync_requested', { repository: 'hololake-knowledge-base' })
|
|
try {
|
|
const result = await invoke<NativeKnowledgeReceipt>('hololake_sync_knowledge')
|
|
trackEvent('hololake_knowledge_sync_completed', { result: 'success' })
|
|
setState((current) => ({
|
|
...current,
|
|
lastCommit: result.commit ?? null,
|
|
phase: 'authenticated',
|
|
}))
|
|
} catch (error) {
|
|
trackEvent('hololake_knowledge_sync_completed', { result: 'error' })
|
|
setState((current) => ({ ...current, error: errorText(error), phase: 'authenticated' }))
|
|
}
|
|
}, [])
|
|
|
|
const logout = useCallback(async () => {
|
|
setState((current) => ({ ...current, error: null, phase: 'busy' }))
|
|
try {
|
|
await invoke('hololake_account_logout')
|
|
trackEvent('hololake_account_logout_completed')
|
|
setState(initialState)
|
|
await refresh()
|
|
} catch (error) {
|
|
setState((current) => ({ ...current, error: errorText(error), phase: 'authenticated' }))
|
|
}
|
|
}, [refresh])
|
|
|
|
return {
|
|
logout,
|
|
requestCode,
|
|
state,
|
|
syncKnowledge,
|
|
verifyCode,
|
|
}
|
|
}
|