feat(ios): add native Git knowledge and device-only secrets
This commit is contained in:
parent
2fca1e4fc3
commit
b01c3d7fa5
51 changed files with 3019 additions and 97 deletions
140
product-source/hololake-platform/src/hooks/useHoloLakeAccount.ts
Normal file
140
product-source/hololake-platform/src/hooks/useHoloLakeAccount.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue