/** * Version management for objects * Handles versioning and hiding of objects * Only the author (pubkey) who published the original note can modify or delete it */ import type { Event } from 'nostr-tools' import { extractTagsFromEvent } from './nostrTagSystem' export interface VersionedObject { event: Event version: number hidden: boolean pubkey: string id: string } /** * Filter events to get only the latest version that is not hidden * Groups events by ID and returns the one with the highest version that is not hidden */ export function getLatestVersion(events: Event[]): Event | null { if (events.length === 0) { return null } const byId = groupVersionedObjectsById(events) const latestById = getLatestVisibleById(byId) const winner = pickHighestVersion(latestById) return winner?.event ?? null } function groupVersionedObjectsById(events: Event[]): Map { const byId = new Map() for (const event of events) { const tags = extractTagsFromEvent(event) if (tags.id) { const list = byId.get(tags.id) ?? [] list.push({ event, version: tags.version, hidden: tags.hidden, pubkey: event.pubkey, id: tags.id, }) byId.set(tags.id, list) } } return byId } function getLatestVisibleById(byId: Map): VersionedObject[] { const latest: VersionedObject[] = [] for (const objects of byId.values()) { const candidate = pickLatestVisible(objects) if (candidate) { latest.push(candidate) } } return latest } function pickLatestVisible(objects: VersionedObject[]): VersionedObject | null { const visible = objects.filter((obj) => !obj.hidden) if (visible.length === 0) { return null } visible.sort((a, b) => b.version - a.version) return visible[0] ?? null } function pickHighestVersion(objects: VersionedObject[]): VersionedObject | null { if (objects.length === 0) { return null } objects.sort((a, b) => b.version - a.version) return objects[0] ?? null } /** * Get all versions of an object (for version history) */ export function getAllVersions(events: Event[]): VersionedObject[] { const versions: VersionedObject[] = [] for (const event of events) { const tags = extractTagsFromEvent(event) if (tags.id) { versions.push({ event, version: tags.version, hidden: tags.hidden, pubkey: event.pubkey, id: tags.id, }) } } // Sort by version (descending) versions.sort((a, b) => b.version - a.version) return versions } /** * Check if a user can modify or delete an object * Only the original author (pubkey) can modify/delete */ export function canModifyObject(event: Event, userPubkey: string): boolean { return event.pubkey === userPubkey } /** * Get the next version number for an object * Finds the highest version and increments it */ export function getNextVersion(events: Event[]): number { if (events.length === 0) { return 0 } let maxVersion = -1 for (const event of events) { const tags = extractTagsFromEvent(event) if (tags.version > maxVersion) { maxVersion = tags.version } } return maxVersion + 1 } /** * Count objects with the same hash ID (for index calculation) */ export function countObjectsWithSameHash(events: Event[], hashId: string): number { return events.filter((event) => { const tags = extractTagsFromEvent(event) return tags.id === hashId }).length }