import { FieldConfig, History, unpackSampleRecord } from '../../convex/engine/historicalObject'; import { useMemo, useRef } from 'react'; export function useHistoricalValue>( fields: FieldConfig, historicalTime: number | undefined, value: T | undefined, history: ArrayBuffer | undefined, ): T | undefined { const manager = useRef(new HistoryManager()); const sampleRecord: Record | undefined = useMemo(() => { if (!value || !history) { return undefined; } if (!(history instanceof ArrayBuffer)) { throw new Error(`Expected ArrayBuffer, found ${typeof history}`); } return unpackSampleRecord(fields, history); }, [value && history]); if (sampleRecord) { manager.current.receive(sampleRecord); } if (value === undefined) { return undefined; } if (!historicalTime) { return value; } const historicalFields = manager.current.query(historicalTime); return { ...value, ...historicalFields }; } class HistoryManager { histories: Record = {}; receive(sampleRecord: Record) { for (const [fieldName, history] of Object.entries(sampleRecord)) { let histories = this.histories[fieldName]; if (!histories) { histories = []; this.histories[fieldName] = histories; } if (histories[histories.length - 1] == history) { continue; } histories.push(history); } } query(historicalTime: number): Record { const result: Record = {}; for (const [fieldName, histories] of Object.entries(this.histories)) { if (histories.length == 0) { continue; } let foundIndex = null; let currentValue = histories[0].initialValue; for (let i = 0; i < histories.length; i++) { const history = histories[i]; for (const sample of history.samples) { if (sample.time > historicalTime) { foundIndex = i; break; } currentValue = sample.value; } if (foundIndex !== null) { break; } } if (foundIndex !== null) { this.histories[fieldName] = histories.slice(foundIndex); } result[fieldName] = currentValue; } return result; } }