LogoutButton.tsx raw

   1  import { cn } from '@/lib/utils'
   2  import { useNostr } from '@/providers/NostrProvider'
   3  import { LogOut } from 'lucide-react'
   4  import { useCallback, useRef, useState } from 'react'
   5  import { useTranslation } from 'react-i18next'
   6  import SidebarItem from './SidebarItem'
   7  
   8  const HOLD_DURATION = 3000
   9  
  10  export default function LogoutButton({ collapse }: { collapse: boolean }) {
  11    const { t } = useTranslation()
  12    const { account, removeAccount } = useNostr()
  13    const [progress, setProgress] = useState(0)
  14    const [isHolding, setIsHolding] = useState(false)
  15    const animationRef = useRef<number | null>(null)
  16    const startTimeRef = useRef<number | null>(null)
  17  
  18    const startHold = useCallback(() => {
  19      if (!account) return
  20      setIsHolding(true)
  21      startTimeRef.current = Date.now()
  22  
  23      const animate = () => {
  24        if (!startTimeRef.current) return
  25        const elapsed = Date.now() - startTimeRef.current
  26        const newProgress = Math.min((elapsed / HOLD_DURATION) * 100, 100)
  27        setProgress(newProgress)
  28  
  29        if (newProgress >= 100) {
  30          // Logout triggered
  31          removeAccount(account)
  32          setProgress(0)
  33          setIsHolding(false)
  34          startTimeRef.current = null
  35          return
  36        }
  37  
  38        animationRef.current = requestAnimationFrame(animate)
  39      }
  40  
  41      animationRef.current = requestAnimationFrame(animate)
  42    }, [account, removeAccount])
  43  
  44    const cancelHold = useCallback(() => {
  45      if (animationRef.current) {
  46        cancelAnimationFrame(animationRef.current)
  47        animationRef.current = null
  48      }
  49      startTimeRef.current = null
  50      setProgress(0)
  51      setIsHolding(false)
  52    }, [])
  53  
  54    if (!account) return null
  55  
  56    return (
  57      <div className="relative">
  58        <SidebarItem
  59          title={t('Logout')}
  60          collapse={collapse}
  61          onTouchStart={startHold}
  62          onTouchEnd={cancelHold}
  63          onTouchCancel={cancelHold}
  64          onMouseDown={startHold}
  65          onMouseUp={cancelHold}
  66          onMouseLeave={cancelHold}
  67          className={cn('select-none', isHolding && 'text-destructive')}
  68        >
  69          <LogOut />
  70        </SidebarItem>
  71        {isHolding && (
  72          <div className="absolute bottom-0 left-0 right-0 h-1 bg-muted rounded-full overflow-hidden">
  73            <div
  74              className="h-full bg-destructive transition-none"
  75              style={{ width: `${progress}%` }}
  76            />
  77          </div>
  78        )}
  79      </div>
  80    )
  81  }
  82