Files
flotilla/src/lib/storage/events.ts
T
2025-09-30 10:52:24 -07:00

103 lines
3.0 KiB
TypeScript

import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage"
import type {TrustedEvent} from "@welshman/util"
import type {Unsubscriber} from "svelte/store"
import {Filesystem, Directory, Encoding} from "@capacitor/filesystem"
import type {Repository, RepositoryUpdate} from "@welshman/relay"
import {on, sortBy} from "@welshman/lib"
export class EventsStorageProvider implements FilesystemStorageProvider {
static filepath = "events.json"
static directory = Directory.Data
static encoding = Encoding.UTF8
limit: number
repository: Repository
rankEvent: (event: TrustedEvent) => number
eventCount: number = 0
isDeleting = false
constructor({
limit,
repository,
rankEvent,
}: {
limit: number
repository: Repository
rankEvent: (event: TrustedEvent) => number
}) {
this.limit = limit
this.repository = repository
this.rankEvent = rankEvent
}
async initializeState(): Promise<void> {
const events = await this.getAll()
this.eventCount = events.length
this.repository.load(events)
}
sync(): Unsubscriber {
const onUpdate = async ({added, removed}: RepositoryUpdate) => {
// Only add events we want to keep
const keep = added.filter(e => this.rankEvent(e) > 0)
// Add new events
if (keep.length > 0) {
await this.updateEvents(keep)
}
// If we're well above our retention limit, drop lowest-ranked events
if (!this.isDeleting && this.eventCount > this.limit * 1.5) {
try {
this.isDeleting = true
for (const event of sortBy(e => -this.rankEvent(e), await this.getAll()).slice(
this.limit,
)) {
removed.add(event.id)
}
if (removed.size > 0) {
await this.deleteEvents(Array.from(removed))
}
} finally {
this.isDeleting = false
}
}
// Keep track of our total number of events. This isn't strictly accurate, but it's close enough
this.eventCount = this.eventCount + keep.length - removed.size
}
return on(this.repository, "update", onUpdate)
}
async getAll(): Promise<TrustedEvent[]> {
return await getAllFromFile(EventsStorageProvider.filepath, EventsStorageProvider.directory, EventsStorageProvider.encoding)
}
async writeAll(events: TrustedEvent[]) {
await Filesystem.writeFile({
path: EventsStorageProvider.filepath,
directory: EventsStorageProvider.directory,
encoding: EventsStorageProvider.encoding,
data: JSON.stringify(events),
})
}
async updateEvents(events: TrustedEvent[]) {
const existing = await this.getAll()
const updated = existing.concat(events)
await this.writeAll(updated)
}
async deleteEvents(ids: string[]) {
const existing = await this.getAll()
const updated = existing.filter(e => !ids.includes(e.id))
await this.writeAll(updated)
}
static async clearStorage(): Promise<void> {
await Filesystem.deleteFile({path: EventsStorageProvider.filepath, directory: EventsStorageProvider.directory})
}
}