import { type ChangeEvent, type FormEvent, useCallback, useRef, useState } from 'react' import { invoke } from '@tauri-apps/api/core' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { isTauri, mockInvoke } from '../mock-tauri' type CloneStatus = 'idle' | 'cloning' | 'error' type CloneAttemptResult = | { ok: true } | { ok: false; errorMessage: string } type CloneRequest = Record & { url: string localPath: string } interface CloneVaultModalProps { open: boolean onClose: () => void onVaultCloned: (path: string, label: string) => void } interface CloneVaultFormState { repoUrl: string localPath: string cloneStatus: CloneStatus cloneError: string | null isCloning: boolean isCloneDisabled: boolean handleClose: () => void handleRepoUrlChange: (event: ChangeEvent) => void handleLocalPathChange: (event: ChangeEvent) => void handleClone: () => Promise } function tauriCall(cmd: string, args: Record): Promise { return isTauri() ? invoke(cmd, args) : mockInvoke(cmd, args) } function repoNameFromUrl(request: Pick): string { const trimmed = request.url.trim().replace(/\/+$/g, '') if (!trimmed) return '' const segment = trimmed.split(/[/:]/).pop() ?? '' return segment.replace(/\.git$/i, '') } function suggestedPathFromUrl(request: Pick): string { const repoName = repoNameFromUrl(request) return repoName ? `~/Vaults/${repoName}` : '' } function labelFromPath(request: Pick): string { const trimmed = request.localPath.trim().replace(/\/+$/g, '') return trimmed.split('/').pop() || 'Vault' } function shouldSyncSuggestedPath(localPath: string, pathDirty: boolean, previousSuggestedPath: string): boolean { return !pathDirty || !localPath.trim() || localPath === previousSuggestedPath } async function attemptClone(request: CloneRequest): Promise { try { await tauriCall('clone_git_repo', request) return { ok: true } } catch (error) { return { ok: false, errorMessage: `Clone failed: ${String(error)}` } } } function useCloneVaultForm(onClose: () => void, onVaultCloned: (path: string, label: string) => void): CloneVaultFormState { const [repoUrl, setRepoUrl] = useState('') const [localPath, setLocalPath] = useState('') const [pathDirty, setPathDirty] = useState(false) const [cloneStatus, setCloneStatus] = useState('idle') const [cloneError, setCloneError] = useState(null) const cloneInFlightRef = useRef(false) const previousSuggestedPathRef = useRef('') const resetState = useCallback(() => { setRepoUrl('') setLocalPath('') setPathDirty(false) setCloneStatus('idle') setCloneError(null) previousSuggestedPathRef.current = '' }, []) const handleClose = useCallback(() => { if (cloneInFlightRef.current) return resetState() onClose() }, [onClose, resetState]) const handleRepoUrlChange = useCallback((event: ChangeEvent) => { const value = event.target.value setRepoUrl(value) setCloneError(null) const nextSuggestedPath = suggestedPathFromUrl({ url: value }) const previousSuggestedPath = previousSuggestedPathRef.current if (shouldSyncSuggestedPath(localPath, pathDirty, previousSuggestedPath)) { setLocalPath(nextSuggestedPath) } previousSuggestedPathRef.current = nextSuggestedPath }, [localPath, pathDirty]) const handleLocalPathChange = useCallback((event: ChangeEvent) => { const value = event.target.value setPathDirty(true) setLocalPath(value) setCloneError(null) }, []) const handleClone = useCallback(async () => { const trimmedUrl = repoUrl.trim() const trimmedPath = localPath.trim() const request = { url: trimmedUrl, localPath: trimmedPath } if (!request.url || !request.localPath || cloneInFlightRef.current) return cloneInFlightRef.current = true setCloneStatus('cloning') setCloneError(null) const result = await attemptClone(request) cloneInFlightRef.current = false if (result.ok) { onVaultCloned(request.localPath, labelFromPath({ localPath: request.localPath })) handleClose() return } setCloneStatus('error') setCloneError(result.errorMessage) }, [handleClose, localPath, onVaultCloned, repoUrl]) const isCloning = cloneStatus === 'cloning' return { repoUrl, localPath, cloneStatus, cloneError, isCloning, isCloneDisabled: !repoUrl.trim() || !localPath.trim() || isCloning, handleClose, handleRepoUrlChange, handleLocalPathChange, handleClone, } } export function CloneVaultModal({ open, onClose, onVaultCloned }: CloneVaultModalProps) { const { repoUrl, localPath, cloneStatus, cloneError, isCloning, isCloneDisabled, handleClose, handleRepoUrlChange, handleLocalPathChange, handleClone, } = useCloneVaultForm(onClose, onVaultCloned) const handleOpenChange = useCallback((isOpen: boolean) => { if (!isOpen && !isCloning) handleClose() }, [handleClose, isCloning]) const handleSubmit = useCallback((event: FormEvent) => { event.preventDefault() void handleClone() }, [handleClone]) return ( Clone Git Repo Clone any remote repository into a local vault folder. Tolaria uses your existing system git configuration for authentication.

{isCloning ? 'Cloning repository… Tolaria will open the vault when git finishes.' : 'SSH keys, the git credential manager, `gh auth`, and other system git auth methods all work.'}

{cloneError && (

{cloneError}

)}
) }