story-research-zapwall/lib/versionManager.ts
2026-01-08 23:53:05 +01:00

146 lines
3.5 KiB
TypeScript

/**
* 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<string, VersionedObject[]> {
const byId = new Map<string, VersionedObject[]>()
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<string, VersionedObject[]>): 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
}