/** * Pure helpers for the timeline device-filter "All / None" behavior. * * The timeline auto-selects every device the first time a device list loads so * the default view shows all data. The subtle bug this guards against (OPL-2983) * is that a naive "auto-select when nothing is selected" effect also re-fires * the instant the user clicks "None" — silently re-selecting everything, so the * None button appears to do nothing. * * We disambiguate "not yet initialized" from "user deliberately cleared" by * remembering the signature of the device list we last initialized for. An * empty selection whose list signature still matches is a deliberate "None"; * an empty selection for a list we have not initialized yet is a fresh load. */ /** Order-independent signature for a device list, keyed by id. */ export function deviceListSignature(deviceIds: readonly string[]): string { return [...deviceIds].sort().join(","); } /** * Decide the initial device selection when a device list appears or changes. * * Returns `null` when no action is needed — either the list is empty or we have * already initialized for this exact list (so an empty selection is a * deliberate "None" and must be left alone). Otherwise returns the new * signature to remember plus `select`: the ids to auto-select (all of them) for * a fresh load, or `null` to record the signature without changing a selection * that was restored from the URL/prior state. */ export function resolveDeviceAutoSelect(params: { deviceIds: readonly string[]; selectedCount: number; initializedSignature: string | null; }): { signature: string; select: string[] | null } | null { const { deviceIds, selectedCount, initializedSignature } = params; if (deviceIds.length === 0) return null; const signature = deviceListSignature(deviceIds); if (signature === initializedSignature) return null; return { signature, select: selectedCount > 0 ? null : [...deviceIds] }; } /** * Whether the user has deliberately deselected every device (the timeline * should render empty). True only once the list has been initialized — so the * brief pre-auto-select render on first load is not mistaken for "None". */ export function isNoDevicesSelected(params: { deviceCount: number; selectedCount: number; currentSignature: string; initializedSignature: string | null; }): boolean { const { deviceCount, selectedCount, currentSignature, initializedSignature } = params; return ( deviceCount > 0 && selectedCount === 0 && initializedSignature === currentSignature ); }