From d4dfd31263c2b29ba3cbe422ef7824e24831fa99 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Tue, 4 Aug 2026 11:37:02 -0400 Subject: [PATCH] Rewrite kind 3063 fetch to use #i tag filtering via direct WebSocket, bypassing NDK grouping/cache issues --- app-stacks/publish_all.py | 2 +- www/app-stacks.html | 103 ++++++++++++++++++++++++++++++-------- www/js/version.json | 6 +-- 3 files changed, 86 insertions(+), 25 deletions(-) diff --git a/app-stacks/publish_all.py b/app-stacks/publish_all.py index 2a6cc84..223ecab 100644 --- a/app-stacks/publish_all.py +++ b/app-stacks/publish_all.py @@ -11,6 +11,7 @@ Usage: import asyncio import json +import os import struct import subprocess import sys @@ -33,7 +34,6 @@ KZAPSTORE_COMMUNITY_PUBKEY = "acfeaea6e51420e8068fac446ca9d17d7a9ef6a5d20d93894e # Load real app developer pubkeys (kind 32267 events) so stack `a` tags # reference events that actually exist on the relay. # Generated by fetching kind 32267 events from wss://relay.zapstore.dev. -import os _PUBKEYS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app_pubkeys.json") try: with open(_PUBKEYS_PATH) as f: diff --git a/www/app-stacks.html b/www/app-stacks.html index 67b7fb8..6d0a58c 100644 --- a/www/app-stacks.html +++ b/www/app-stacks.html @@ -206,6 +206,8 @@ let appVersions = {}; // identifier -> { version, size, url, created_at } let assetSub = null; let assetsLoaded = false; + let assetEventCount = 0; // total kind 3063 events received + let assetOldestTimestamp = Infinity; // oldest raw event timestamp seen (for pagination) /* AUTH STATE MODEL (Template reference) @@ -788,7 +790,8 @@ console.log('[app-stacks] Added app def:', apps[apps.length - 1].name); if (appDefsLoaded) renderApps(); }); - window.addEventListener('ndkEose', () => { + window.addEventListener('ndkEose', (event) => { + if (appDefSub && event.detail && event.detail.subId !== appDefSub.subId) return; if (!appDefsLoaded) { appDefsLoaded = true; renderApps(); } }); } @@ -823,7 +826,8 @@ console.log('[app-stacks] Added stack:', stacks[stacks.length - 1].name); if (stacksLoaded) renderApps(); }); - window.addEventListener('ndkEose', function() { + window.addEventListener('ndkEose', function(event) { + if (stackSub && event.detail && event.detail.subId !== stackSub.subId) return; if (!stacksLoaded) { stacksLoaded = true; renderApps(); } }); } @@ -841,27 +845,84 @@ return { identifier, version, size, url: urls[0] || '', created_at: evt.created_at || 0 }; } + // Direct WebSocket fetch of kind 3063 events from relay.zapstore.dev. + // Uses #i tag filtering to request only events for our specific apps. + // Bypasses NDK to avoid grouping/cache issues. + var ZAPSTORE_RELAY = 'wss://relay.zapstore.dev'; + function subscribeAppAssets() { - console.log('[app-stacks] Subscribing to app assets (kind 3063)...'); - assetSub = subscribe({ kinds: [3063], limit: 500 }, { closeOnEose: false, cacheUsage: 'CACHE_FIRST' }); + // Wait for app definitions to load so we know the identifiers + if (!appDefsLoaded || apps.length === 0) { + setTimeout(subscribeAppAssets, 1000); + return; + } + console.log('[app-stacks] Fetching kind 3063 events for ' + apps.length + ' apps from ' + ZAPSTORE_RELAY); + fetchAppAssetsByIdentifier(); } - function initAppAssetListener() { - window.addEventListener('ndkEvent', function(event) { - var evt = event.detail; - if (evt.kind !== 3063) return; - var parsed = parseAppAsset(evt); - if (!parsed.identifier) return; - // Keep the latest version (highest created_at) - var existing = appVersions[parsed.identifier]; - if (!existing || parsed.created_at > existing.created_at) { - appVersions[parsed.identifier] = parsed; - if (assetsLoaded) renderApps(); + function fetchAppAssetsByIdentifier() { + var identifiers = apps.map(function(a) { return a.identifier; }); + var batchSize = 50; + var batches = []; + for (var i = 0; i < identifiers.length; i += batchSize) { + batches.push(identifiers.slice(i, i + batchSize)); + } + console.log('[app-stacks] Fetching in ' + batches.length + ' batches of up to ' + batchSize + ' apps each'); + + var batchIndex = 0; + function fetchNextBatch() { + if (batchIndex >= batches.length) { + if (!assetsLoaded) { + assetsLoaded = true; + renderApps(); + } + console.log('[app-stacks] Kind 3063 fetch complete. Total events: ' + assetEventCount + ', unique apps with versions: ' + Object.keys(appVersions).length); + return; } - }); - window.addEventListener('ndkEose', function() { - if (!assetsLoaded) { assetsLoaded = true; renderApps(); } - }); + + var batch = batches[batchIndex]; + batchIndex++; + var reqId = 'assets-' + batchIndex + '-' + Date.now(); + console.log('[app-stacks] Fetching batch ' + batchIndex + '/' + batches.length + ' (' + batch.length + ' apps)...'); + + var ws = new WebSocket(ZAPSTORE_RELAY); + ws.onopen = function() { + ws.send(JSON.stringify(['REQ', reqId, { kinds: [3063], '#i': batch, limit: 500 }])); + }; + ws.onmessage = function(ev) { + var msg = JSON.parse(ev.data); + if (msg[0] === 'EVENT' && msg[1] === reqId) { + var evt = msg[2]; + assetEventCount++; + var parsed = parseAppAsset(evt); + if (parsed.identifier) { + var existing = appVersions[parsed.identifier]; + if (!existing || parsed.created_at > existing.created_at) { + appVersions[parsed.identifier] = parsed; + if (assetsLoaded) renderApps(); + } + } + } else if (msg[0] === 'EOSE' && msg[1] === reqId) { + ws.send(JSON.stringify(['CLOSE', reqId])); + ws.close(); + console.log('[app-stacks] Batch ' + batchIndex + ' complete (total events: ' + assetEventCount + ', unique apps: ' + Object.keys(appVersions).length + ')'); + if (!assetsLoaded) { + assetsLoaded = true; + renderApps(); + } + setTimeout(fetchNextBatch, 100); + } + }; + ws.onerror = function(err) { + console.error('[app-stacks] WebSocket error in batch ' + batchIndex + ':', err); + if (!assetsLoaded) { + assetsLoaded = true; + renderApps(); + } + setTimeout(fetchNextBatch, 100); + }; + } + fetchNextBatch(); } async function publishAppStack(name, description) { @@ -970,8 +1031,8 @@ initAppStackListener(); subscribeAppStacks(); - // Set up app-asset (kind 3063) listener and subscription for version info - initAppAssetListener(); + // Fetch kind 3063 (Software Asset) events directly from relay.zapstore.dev + // for version info. Bypasses NDK to avoid grouping/cache issues. subscribeAppAssets(); // Optional UX note for public mode pages. diff --git a/www/js/version.json b/www/js/version.json index 2f4fbd2..a4e10e9 100644 --- a/www/js/version.json +++ b/www/js/version.json @@ -1,5 +1,5 @@ { - "VERSION": "v0.7.97", - "VERSION_NUMBER": "0.7.97", - "BUILD_DATE": "2026-08-04T13:32:52.287Z" + "VERSION": "v0.7.98", + "VERSION_NUMBER": "0.7.98", + "BUILD_DATE": "2026-08-04T15:37:02.089Z" }