From 9b12cbcb6fd26076d7f72224bb0c9e24d224270c Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 3 Jan 2026 13:42:28 -0500 Subject: [PATCH] Switch watch history schemas to month identifiers --- js/nostrEventSchemas.js | 93 +++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/js/nostrEventSchemas.js b/js/nostrEventSchemas.js index d4926454..93905faa 100644 --- a/js/nostrEventSchemas.js +++ b/js/nostrEventSchemas.js @@ -353,39 +353,37 @@ const BASE_SCHEMAS = { }, [NOTE_TYPES.WATCH_HISTORY_INDEX]: { type: NOTE_TYPES.WATCH_HISTORY_INDEX, - label: "Watch history index", + label: "Watch history month index", kind: WATCH_HISTORY_KIND, identifierTag: { name: "d", - value: WATCH_HISTORY_LIST_IDENTIFIER, }, - snapshotTagName: "snapshot", - totalTagName: "chunks", - chunkPointerTagName: "a", + monthTagName: "month", + headTag: { name: "head", value: "month" }, + monthPointerTagName: "a", appendTags: DEFAULT_APPEND_TAGS, content: { format: "json", description: - "JSON payload describing the active snapshot and chunk count.", + "JSON payload listing watched event identifiers for a given YYYY-MM month; entries may include optional watchedAt metadata.", }, }, [NOTE_TYPES.WATCH_HISTORY_CHUNK]: { type: NOTE_TYPES.WATCH_HISTORY_CHUNK, - label: "Watch history snapshot", + label: "Watch history month payload", kind: WATCH_HISTORY_KIND, identifierTag: { name: "d", }, encryptionTag: { name: "encrypted", values: ["nip44_v2", "nip44", "nip04"] }, - snapshotTagName: "snapshot", - chunkTagName: "chunk", - headTag: { name: "head", value: "1" }, + monthTagName: "month", + headTag: { name: "head", value: "month" }, headTagIndex: 2, appendTags: DEFAULT_APPEND_TAGS, content: { format: "encrypted-json", description: - "Encrypted JSON payload containing chunked watch history entries.", + "Encrypted JSON payload containing a month's watched event identifiers with optional watchedAt metadata.", }, }, [NOTE_TYPES.SUBSCRIPTION_LIST]: { @@ -1351,8 +1349,10 @@ export function buildCommentEvent({ export function buildWatchHistoryIndexEvent({ pubkey, created_at, + monthIdentifier, snapshotId, - totalChunks, + isHead = false, + monthAddresses = [], chunkAddresses = [], additionalTags = [], content, @@ -1360,25 +1360,32 @@ export function buildWatchHistoryIndexEvent({ const schema = getNostrEventSchema(NOTE_TYPES.WATCH_HISTORY_INDEX); const tags = []; const identifierName = schema?.identifierTag?.name || "d"; - const identifierValue = schema?.identifierTag?.value || WATCH_HISTORY_LIST_IDENTIFIER; + const identifierValue = + (typeof monthIdentifier === "string" && monthIdentifier.trim()) || + (typeof snapshotId === "string" && snapshotId.trim()) || + schema?.identifierTag?.value || + WATCH_HISTORY_LIST_IDENTIFIER; if (identifierName && identifierValue) { tags.push([identifierName, identifierValue]); } - const snapshotTagName = schema?.snapshotTagName || "snapshot"; - if (snapshotTagName && snapshotId) { - tags.push([snapshotTagName, snapshotId]); + const monthTagName = schema?.monthTagName || "month"; + if (monthTagName && identifierValue) { + tags.push([monthTagName, identifierValue]); } - const totalTagName = schema?.totalTagName || "chunks"; - if (totalTagName && Number.isFinite(totalChunks)) { - tags.push([totalTagName, String(Math.max(0, Math.floor(totalChunks)))]); - } - const pointerTagName = schema?.chunkPointerTagName || "a"; - chunkAddresses.forEach((address) => { + const pointerTagName = schema?.monthPointerTagName || schema?.chunkPointerTagName || "a"; + const resolvedMonthAddresses = Array.isArray(monthAddresses) && monthAddresses.length + ? monthAddresses + : chunkAddresses; + resolvedMonthAddresses?.forEach((address) => { if (typeof address === "string" && address) { tags.push([pointerTagName, address]); } }); + if (isHead && schema?.headTag?.name && schema?.headTag?.value) { + tags.unshift([schema.headTag.name, schema.headTag.value]); + } + appendSchemaTags(tags, schema); const sanitizedAdditionalTags = sanitizeAdditionalTags(additionalTags); if (sanitizedAdditionalTags.length) { @@ -1387,12 +1394,12 @@ export function buildWatchHistoryIndexEvent({ let resolvedContent = content; if (resolvedContent === undefined) { - const payload = {}; - if (snapshotId) { - payload.snapshot = snapshotId; - } - if (Number.isFinite(totalChunks)) { - payload.totalChunks = Math.max(0, Math.floor(totalChunks)); + const payload = { + month: identifierValue || "", + items: [], + }; + if (isHead) { + payload.head = true; } resolvedContent = JSON.stringify(payload); } else if (typeof resolvedContent !== "string") { @@ -1414,19 +1421,21 @@ export function buildWatchHistoryIndexEvent({ export function buildWatchHistoryChunkEvent({ pubkey, created_at, - chunkIdentifier, + monthIdentifier, snapshotId, - chunkIndex, - totalChunks, pointerTags = [], - chunkAddresses = [], + isHead = false, + chunkIndex, content, encryption, }) { const schema = getNostrEventSchema(NOTE_TYPES.WATCH_HISTORY_CHUNK); const tags = []; const identifierName = schema?.identifierTag?.name || "d"; - const identifierValue = chunkIdentifier || schema?.identifierTag?.value; + const identifierValue = + (typeof monthIdentifier === "string" && monthIdentifier.trim()) || + (typeof snapshotId === "string" && snapshotId.trim()) || + schema?.identifierTag?.value; if (identifierName && identifierValue) { tags.push([identifierName, identifierValue]); } @@ -1448,13 +1457,9 @@ export function buildWatchHistoryChunkEvent({ if (encryptionTagName && resolvedEncryptionTag) { tags.push([encryptionTagName, resolvedEncryptionTag]); } - const snapshotTagName = schema?.snapshotTagName; - if (snapshotTagName && snapshotId) { - tags.push([snapshotTagName, snapshotId]); - } - const chunkTagName = schema?.chunkTagName; - if (chunkTagName && typeof chunkIndex === "number" && typeof totalChunks === "number") { - tags.push([chunkTagName, String(chunkIndex), String(totalChunks)]); + const monthTagName = schema?.monthTagName || "month"; + if (monthTagName && identifierValue) { + tags.push([monthTagName, identifierValue]); } pointerTags.forEach((tag) => { if (Array.isArray(tag) && tag.length >= 2) { @@ -1462,16 +1467,12 @@ export function buildWatchHistoryChunkEvent({ } }); - if (chunkIndex === 0 && schema?.headTag?.name && schema?.headTag?.value) { + const shouldMarkHead = isHead === true || chunkIndex === 0; + if (shouldMarkHead && schema?.headTag?.name && schema?.headTag?.value) { const insertionIndex = Number.isInteger(schema?.headTagIndex) ? Math.max(0, Math.min(tags.length, schema.headTagIndex)) : Math.min(tags.length, 2); tags.splice(insertionIndex, 0, [schema.headTag.name, schema.headTag.value]); - chunkAddresses.forEach((address) => { - if (typeof address === "string" && address) { - tags.push(["a", address]); - } - }); } appendSchemaTags(tags, schema);