acbe0ad5ca
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import type { EntityObject } from '@ChronoManager/models/entity'
|
|
import type {
|
|
CalendarOccurrence,
|
|
CalendarOccurrenceIndex,
|
|
} from '../types/occurrence'
|
|
import type { VisibleDateRange } from '../types'
|
|
import { shiftLocalDays, startOfLocalDay } from './dateRanges'
|
|
import { expandEntityOccurrences } from './recurrence'
|
|
|
|
function appendToDayBuckets(index: CalendarOccurrenceIndex, occurrence: CalendarOccurrence) {
|
|
const lastOccupiedDayMs = startOfLocalDay(new Date(occurrence.endMs - 1)).getTime()
|
|
let day = new Date(occurrence.dayStartMs)
|
|
|
|
while (day.getTime() <= lastOccupiedDayMs) {
|
|
const dayKey = day.getTime()
|
|
const bucket = index.byDay.get(dayKey)
|
|
if (bucket) bucket.push(occurrence)
|
|
else index.byDay.set(dayKey, [occurrence])
|
|
day = startOfLocalDay(shiftLocalDays(day, 1))
|
|
}
|
|
}
|
|
|
|
function sortOccurrences(occurrences: CalendarOccurrence[]) {
|
|
occurrences.sort((left, right) => (
|
|
left.startMs - right.startMs
|
|
|| right.durationMs - left.durationMs
|
|
|| left.key.localeCompare(right.key)
|
|
))
|
|
}
|
|
|
|
export function buildOccurrenceIndex(
|
|
entities: EntityObject[],
|
|
range?: VisibleDateRange,
|
|
): CalendarOccurrenceIndex {
|
|
const index: CalendarOccurrenceIndex = {
|
|
byDay: new Map(),
|
|
byEntity: new Map(),
|
|
sorted: [],
|
|
}
|
|
|
|
for (const entity of entities) {
|
|
const entityOccurrences = expandEntityOccurrences(entity, range)
|
|
if (entityOccurrences.length === 0) continue
|
|
|
|
index.byEntity.set(String(entity.identifier), entityOccurrences)
|
|
for (const occurrence of entityOccurrences) {
|
|
index.sorted.push(occurrence)
|
|
appendToDayBuckets(index, occurrence)
|
|
}
|
|
}
|
|
|
|
sortOccurrences(index.sorted)
|
|
for (const bucket of index.byDay.values()) sortOccurrences(bucket)
|
|
|
|
return index
|
|
}
|
|
|
|
export function occurrencesForDay(
|
|
index: CalendarOccurrenceIndex,
|
|
date: Date | number,
|
|
): CalendarOccurrence[] {
|
|
const dayKey = startOfLocalDay(new Date(date)).getTime()
|
|
return index.byDay.get(dayKey) ?? []
|
|
}
|
|
|
|
export function occurrencesForRange(
|
|
index: CalendarOccurrenceIndex,
|
|
range: VisibleDateRange,
|
|
): CalendarOccurrence[] {
|
|
if (range.endMs <= range.startMs) return []
|
|
|
|
const matches = new Map<string, CalendarOccurrence>()
|
|
let day = startOfLocalDay(new Date(range.startMs))
|
|
|
|
while (day.getTime() < range.endMs) {
|
|
for (const occurrence of index.byDay.get(day.getTime()) ?? []) {
|
|
if (occurrence.startMs < range.endMs && occurrence.endMs > range.startMs) {
|
|
matches.set(occurrence.key, occurrence)
|
|
}
|
|
}
|
|
day = startOfLocalDay(shiftLocalDays(day, 1))
|
|
}
|
|
|
|
const occurrences = Array.from(matches.values())
|
|
sortOccurrences(occurrences)
|
|
return occurrences
|
|
}
|