2026-01-13 14:49:19 +01:00

155 lines
4.7 KiB
TypeScript

import { nostrAuthService } from '@/lib/nostrAuth'
import { keyManagementService } from '@/lib/keyManagement'
import { t } from '@/lib/i18n'
import type { PublicKeys } from './types'
import { extractKeyFromInput, isValidPrivateKeyFormat } from './keyImportParsing'
export interface KeyManagementManagerState {
publicKeys: PublicKeys | null
accountExists: boolean
loading: boolean
error: string | null
importKey: string
importing: boolean
showImportForm: boolean
showReplaceWarning: boolean
recoveryPhrase: string[] | null
newNpub: string | null
copiedNpub: boolean
copiedPublicKey: boolean
copiedRecoveryPhrase: boolean
}
export type PatchState = (patch: Partial<KeyManagementManagerState>) => void
export const INITIAL_KEY_MANAGEMENT_STATE: KeyManagementManagerState = {
publicKeys: null,
accountExists: false,
loading: true,
error: null,
importKey: '',
importing: false,
showImportForm: false,
showReplaceWarning: false,
recoveryPhrase: null,
newNpub: null,
copiedNpub: false,
copiedPublicKey: false,
copiedRecoveryPhrase: false,
}
export async function loadKeys(params: { patchState: PatchState }): Promise<void> {
try {
params.patchState({ loading: true, error: null })
const exists = await nostrAuthService.accountExists()
params.patchState({ accountExists: exists })
if (!exists) {
params.patchState({ publicKeys: null })
return
}
const keys = await keyManagementService.getPublicKeys()
params.patchState({ publicKeys: keys ?? null })
} catch (e) {
const errorMessage = e instanceof Error ? e.message : t('settings.keyManagement.loading')
params.patchState({ error: errorMessage })
console.error('[KeyManagement] Error loading keys:', e)
} finally {
params.patchState({ loading: false })
}
}
export async function handleImport(params: {
state: KeyManagementManagerState
patchState: PatchState
}): Promise<void> {
const extractedKey = validateAndExtractImportKey({ importKey: params.state.importKey, patchState: params.patchState })
if (!extractedKey) {
return
}
if (params.state.accountExists) {
params.patchState({ showReplaceWarning: true })
return
}
await performImport({ key: extractedKey, accountExists: false, patchState: params.patchState })
}
export async function confirmReplace(params: {
state: KeyManagementManagerState
patchState: PatchState
}): Promise<void> {
const extractedKey = validateAndExtractImportKey({ importKey: params.state.importKey, patchState: params.patchState })
if (!extractedKey) {
return
}
await performImport({ key: extractedKey, accountExists: true, patchState: params.patchState })
}
function validateAndExtractImportKey(params: { importKey: string; patchState: PatchState }): string | null {
if (!params.importKey.trim()) {
params.patchState({ error: t('settings.keyManagement.import.error.required') })
return null
}
const extractedKey = extractKeyFromInput(params.importKey)
if (!extractedKey) {
params.patchState({ error: t('settings.keyManagement.import.error.invalid') })
return null
}
if (!isValidPrivateKeyFormat(extractedKey)) {
params.patchState({ error: t('settings.keyManagement.import.error.invalid') })
return null
}
return extractedKey
}
async function performImport(params: {
key: string
accountExists: boolean
patchState: PatchState
}): Promise<void> {
try {
params.patchState({ importing: true, error: null, showReplaceWarning: false })
if (params.accountExists) {
await nostrAuthService.deleteAccount()
}
const result = await nostrAuthService.createAccount(params.key)
params.patchState({
recoveryPhrase: result.recoveryPhrase,
newNpub: result.npub,
importKey: '',
showImportForm: false,
})
await loadKeys({ patchState: params.patchState })
await maybeStartUserSync({ publicKey: result.publicKey })
} catch (e) {
const errorMessage = e instanceof Error ? e.message : t('settings.keyManagement.import.error.failed')
params.patchState({ error: errorMessage })
console.error('[KeyManagement] Error importing key:', e)
} finally {
params.patchState({ importing: false })
}
}
async function maybeStartUserSync(params: { publicKey: string | undefined }): Promise<void> {
if (!params.publicKey) {
return
}
const { swClient } = await import('@/lib/swClient')
const isReady = await swClient.isReady()
if (isReady) {
void swClient.startUserSync(params.publicKey)
}
}
export async function copyToClipboard(params: {
text: string
onCopied: () => void
onCopyFailed: (error: unknown) => void
}): Promise<void> {
try {
await navigator.clipboard.writeText(params.text)
params.onCopied()
} catch (e) {
params.onCopyFailed(e)
}
}