import { useEffect, useRef, useState } from 'preact/hooks'; import { DroppedFramesEvent, getComponentName, getEventSeverity, InteractionEvent, } from './data'; import { SlowdownHistoryItem } from './slowdown-history'; import { ChevronRight } from './icons'; import { cn } from '~web/utils/helpers'; export type CollapsedDroppedFrame = { kind: 'collapsed-frame-drops'; events: Array; timestamp: number; }; type CollapsedKeyboardInput = { kind: 'collapsed-keyboard'; events: Array; timestamp: number; }; const useNestedFlash = ({ flashingItemsCount, totalEvents, }: { totalEvents: number; // this breaks if you have constant 1 item flashing, but the actual item is different over time (it's fine for now) flashingItemsCount: number; }) => { const [newFlash, setNewFlash] = useState(false); const flashedFor = useRef(0); const lastFlashTime = useRef(0); // oxlint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { if (flashedFor.current >= totalEvents) { return; } const now = Date.now(); const debounceTime = 250; const timeSinceLastFlash = now - lastFlashTime.current; if (timeSinceLastFlash >= debounceTime) { setNewFlash(false); const timeout = setTimeout(() => { flashedFor.current = totalEvents; lastFlashTime.current = Date.now(); setNewFlash(true); // horrible, don't look at this, move along setTimeout(() => { setNewFlash(false); }, 2000); }, 50); return () => clearTimeout(timeout); } else { const delayNeeded = debounceTime - timeSinceLastFlash; const timeout = setTimeout(() => { setNewFlash(false); setTimeout(() => { flashedFor.current = totalEvents; lastFlashTime.current = Date.now(); setNewFlash(true); // horrible, don't look at this, move along setTimeout(() => { setNewFlash(false); }, 2000); }, 50); }, delayNeeded); return () => clearTimeout(timeout); } }, [flashingItemsCount]); return newFlash; }; export const CollapsedItem = ({ item, shouldFlash, }: { item: CollapsedDroppedFrame | CollapsedKeyboardInput; shouldFlash: (id: string) => boolean; }) => { const [expanded, setExpanded] = useState(false); const severity = item.events.map(getEventSeverity).reduce((prev, curr) => { switch (curr) { case 'high': { return 'high'; } case 'needs-improvement': { return prev === 'high' ? 'high' : 'needs-improvement'; } case 'low': { return prev; } } }, 'low'); const flashingItemsCount = item.events.reduce( (prev, curr) => (shouldFlash(curr.id) ? prev + 1 : prev), 0, ); const shouldFlashAgain = useNestedFlash({ flashingItemsCount, totalEvents: item.events.length, }); return (
{expanded && ( {item.events .toSorted((a, b) => b.timestamp - a.timestamp) .map((event) => ( ))} )}
); }; const IndentedContent = ({ children, }: { children: JSX.Element | JSX.Element[] }) => (
{children}
);