story-research-zapwall/lib/versionManager.ts
2026-01-06 00:26:31 +01:00

140 lines
3.3 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
}
// Group by ID and find the latest non-hidden version
const byId = new Map<string, VersionedObject[]>()
for (const event of events) {
const tags = extractTagsFromEvent(event)
if (!tags.id) {
continue
}
if (!byId.has(tags.id)) {
byId.set(tags.id, [])
}
byId.get(tags.id)!.push({
event,
version: tags.version,
hidden: tags.hidden,
pubkey: event.pubkey,
id: tags.id,
})
}
// For each ID, find the latest non-hidden version
const latestVersions: VersionedObject[] = []
for (const objects of byId.values()) {
// Filter out hidden objects
const visible = objects.filter((obj) => !obj.hidden)
if (visible.length === 0) {
// All versions are hidden, skip this object
continue
}
// Sort by version (descending) and take the first (latest)
visible.sort((a, b) => b.version - a.version)
latestVersions.push(visible[0])
}
// If we have multiple IDs, we need to return the one with the highest version
// But typically we expect one ID per query, so return the first
if (latestVersions.length === 0) {
return null
}
// Sort by version and return the latest
latestVersions.sort((a, b) => b.version - a.version)
return latestVersions[0].event
}
/**
* 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) {
continue
}
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
}