* esm * wip * wip * wip * wip * wip * wip * subscription notice * wip * wip * wip * fix envs * fix: update docker build * fix * esm/types * delete dashboard :D * add patches to dockerfiles * update packages + catalogs + ts * wip * remove native libs * ts * improvements * fix redirects and fetching session * try fix favicon * fixes * fix * order and resize reportds within a dashboard * improvements * wip * added userjot to dashboard * fix * add op * wip * different cache key * improve date picker * fix table * event details loading * redo onboarding completely * fix login * fix * fix * extend session, billing and improve bars * fix * reduce price on 10M
82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
import { TooltipComplete } from '@/components/tooltip-complete';
|
|
import { useDebounceState } from '@/hooks/use-debounce-state';
|
|
import useWS from '@/hooks/use-ws';
|
|
import { useTRPC } from '@/integrations/trpc/react';
|
|
import { cn } from '@/utils/cn';
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { useEffect, useRef } from 'react';
|
|
import { toast } from 'sonner';
|
|
import { AnimatedNumber } from '../animated-number';
|
|
|
|
export interface LiveCounterProps {
|
|
projectId: string;
|
|
}
|
|
|
|
const FIFTEEN_SECONDS = 1000 * 30;
|
|
|
|
export function LiveCounter({ projectId }: LiveCounterProps) {
|
|
const trpc = useTRPC();
|
|
const client = useQueryClient();
|
|
const counter = useDebounceState(0, 1000);
|
|
const lastRefresh = useRef(Date.now());
|
|
const query = useQuery(
|
|
trpc.overview.liveVisitors.queryOptions({
|
|
projectId,
|
|
}),
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (query.data) {
|
|
counter.set(query.data);
|
|
}
|
|
}, [query.data]);
|
|
|
|
useWS<number>(
|
|
`/live/visitors/${projectId}`,
|
|
(value) => {
|
|
if (!Number.isNaN(value)) {
|
|
counter.set(value);
|
|
if (Date.now() - lastRefresh.current > FIFTEEN_SECONDS) {
|
|
lastRefresh.current = Date.now();
|
|
if (!document.hidden) {
|
|
toast('Refreshed data');
|
|
client.refetchQueries({
|
|
type: 'active',
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
debounce: {
|
|
delay: 1000,
|
|
maxWait: 5000,
|
|
},
|
|
},
|
|
);
|
|
|
|
return (
|
|
<TooltipComplete
|
|
content={`${counter.debounced} unique visitors last 5 minutes`}
|
|
>
|
|
<div className="flex h-8 items-center gap-2 rounded border border-border px-3 font-medium leading-none">
|
|
<div className="relative">
|
|
<div
|
|
className={cn(
|
|
'h-3 w-3 animate-ping rounded-full bg-emerald-500 opacity-100 transition-all',
|
|
counter.debounced === 0 && 'bg-destructive opacity-0',
|
|
)}
|
|
/>
|
|
<div
|
|
className={cn(
|
|
'absolute left-0 top-0 h-3 w-3 rounded-full bg-emerald-500 transition-all',
|
|
counter.debounced === 0 && 'bg-destructive',
|
|
)}
|
|
/>
|
|
</div>
|
|
<AnimatedNumber value={counter.debounced} />
|
|
</div>
|
|
</TooltipComplete>
|
|
);
|
|
}
|