PasswordPromptProvider.tsx raw

   1  import {
   2    AlertDialog,
   3    AlertDialogAction,
   4    AlertDialogCancel,
   5    AlertDialogContent,
   6    AlertDialogDescription,
   7    AlertDialogFooter,
   8    AlertDialogHeader,
   9    AlertDialogTitle
  10  } from '@/components/ui/alert-dialog'
  11  import { Input } from '@/components/ui/input'
  12  import { createContext, useCallback, useContext, useRef, useState } from 'react'
  13  import { useTranslation } from 'react-i18next'
  14  
  15  type PasswordPromptContextType = {
  16    promptPassword: (message: string) => Promise<string | null>
  17  }
  18  
  19  const PasswordPromptContext = createContext<PasswordPromptContextType | undefined>(undefined)
  20  
  21  export const usePasswordPrompt = () => {
  22    const context = useContext(PasswordPromptContext)
  23    if (!context) {
  24      throw new Error('usePasswordPrompt must be used within PasswordPromptProvider')
  25    }
  26    return context
  27  }
  28  
  29  export function PasswordPromptProvider({ children }: { children: React.ReactNode }) {
  30    const { t } = useTranslation()
  31    const [open, setOpen] = useState(false)
  32    const [message, setMessage] = useState('')
  33    const [password, setPassword] = useState('')
  34    const resolverRef = useRef<((value: string | null) => void) | null>(null)
  35  
  36    const promptPassword = useCallback((msg: string): Promise<string | null> => {
  37      return new Promise((resolve) => {
  38        setMessage(msg)
  39        setPassword('')
  40        setOpen(true)
  41        resolverRef.current = resolve
  42      })
  43    }, [])
  44  
  45    const handleConfirm = () => {
  46      setOpen(false)
  47      resolverRef.current?.(password)
  48      resolverRef.current = null
  49    }
  50  
  51    const handleCancel = () => {
  52      setOpen(false)
  53      resolverRef.current?.(null)
  54      resolverRef.current = null
  55    }
  56  
  57    return (
  58      <PasswordPromptContext.Provider value={{ promptPassword }}>
  59        {children}
  60        <AlertDialog open={open} onOpenChange={(o) => !o && handleCancel()}>
  61          <AlertDialogContent>
  62            <AlertDialogHeader>
  63              <AlertDialogTitle>{t('Password Required')}</AlertDialogTitle>
  64              <AlertDialogDescription>{message}</AlertDialogDescription>
  65            </AlertDialogHeader>
  66            <Input
  67              type="password"
  68              value={password}
  69              onChange={(e) => setPassword(e.target.value)}
  70              placeholder={t('Enter password')}
  71              autoFocus
  72              onKeyDown={(e) => {
  73                if (e.key === 'Enter') {
  74                  e.preventDefault()
  75                  handleConfirm()
  76                }
  77              }}
  78            />
  79            <AlertDialogFooter>
  80              <AlertDialogCancel onClick={handleCancel}>{t('Cancel')}</AlertDialogCancel>
  81              <AlertDialogAction onClick={handleConfirm}>{t('Confirm')}</AlertDialogAction>
  82            </AlertDialogFooter>
  83          </AlertDialogContent>
  84        </AlertDialog>
  85      </PasswordPromptContext.Provider>
  86    )
  87  }
  88