46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
|
|
import { useMemo } from "react"
|
||
|
|
import { cn } from "@/lib/utils"
|
||
|
|
|
||
|
|
interface RefreshControlProps {
|
||
|
|
lastUpdated: string | null
|
||
|
|
isConnected: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatRelativeTime(timestamp: string): string {
|
||
|
|
const diff = Date.now() - new Date(timestamp).getTime()
|
||
|
|
const seconds = Math.floor(diff / 1000)
|
||
|
|
if (seconds < 5) return "just now"
|
||
|
|
if (seconds < 60) return `${seconds}s ago`
|
||
|
|
const minutes = Math.floor(seconds / 60)
|
||
|
|
return `${minutes}m ago`
|
||
|
|
}
|
||
|
|
|
||
|
|
export function RefreshControl({ lastUpdated, isConnected }: RefreshControlProps) {
|
||
|
|
const relativeTime = useMemo(() => {
|
||
|
|
if (!lastUpdated) return "..."
|
||
|
|
return formatRelativeTime(lastUpdated)
|
||
|
|
}, [lastUpdated])
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="flex items-center gap-2 rounded-lg border px-3 py-1.5">
|
||
|
|
<span
|
||
|
|
className={cn(
|
||
|
|
"relative flex size-2",
|
||
|
|
isConnected ? "text-green-500" : "text-muted-foreground"
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
{isConnected && (
|
||
|
|
<span className="absolute inline-flex size-full animate-ping rounded-full bg-green-400 opacity-75" />
|
||
|
|
)}
|
||
|
|
<span
|
||
|
|
className={cn(
|
||
|
|
"relative inline-flex size-2 rounded-full",
|
||
|
|
isConnected ? "bg-green-500" : "bg-muted-foreground"
|
||
|
|
)}
|
||
|
|
/>
|
||
|
|
</span>
|
||
|
|
<span className="text-xs text-muted-foreground">{relativeTime}</span>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|