This commit is contained in:
Laan Tungir
2026-06-14 08:07:35 -04:00
parent eed00f67cf
commit 38661b7fb6
3 changed files with 159 additions and 18 deletions

View File

@@ -342,6 +342,7 @@
<div class="panelTitle">Kinds</div>
<div class="btnRow">
<button id="btnRefreshKinds" class="btnMini" type="button">Refresh Cache</button>
<button id="btnGetAllEvents" class="btnMini" type="button">Get all events</button>
</div>
<div id="divKindsStatus" class="hint">Loading...</div>
<div id="divKindsList" class="listWrap"></div>
@@ -683,6 +684,9 @@
let selectedKindSub = null;
let currentDetailEvent = null;
let lastDecryptResult = null;
let allEventsFetchSub = null;
let allEventsFetchInFlight = false;
let allEventsFetchTimeoutId = null;
const eventsByKind = new Map(); // kind -> Map(eventKey, event)
const selectedEventKeys = new Set();
@@ -703,6 +707,7 @@
const divFooterRight = document.getElementById('divFooterRight');
const btnRefreshKinds = document.getElementById('btnRefreshKinds');
const btnGetAllEvents = document.getElementById('btnGetAllEvents');
const divKindsStatus = document.getElementById('divKindsStatus');
const divKindsList = document.getElementById('divKindsList');
@@ -1396,16 +1401,22 @@
};
}
function getCachedKindsAndEventTotals() {
const kinds = getKindsSorted();
const totalEvents = kinds.reduce((sum, k) => sum + getEventsForKind(k).length, 0);
return { kindsCount: kinds.length, totalEvents };
}
function renderKinds() {
const kinds = getKindsSorted();
if (!kinds.length) {
divKindsList.innerHTML = '<div class="hint">No cached events found.</div>';
divKindsStatus.textContent = '0 kinds · 0 events';
if (!allEventsFetchInFlight) divKindsStatus.textContent = '0 kinds · 0 events';
return;
}
const totalEvents = kinds.reduce((sum, k) => sum + getEventsForKind(k).length, 0);
divKindsStatus.textContent = `${kinds.length} kinds · ${totalEvents.toLocaleString()} cached events`;
if (!allEventsFetchInFlight) divKindsStatus.textContent = `${kinds.length} kinds · ${totalEvents.toLocaleString()} cached events`;
divKindsList.innerHTML = kinds.map((kind) => {
const count = getEventsForKind(kind).length;
@@ -1682,6 +1693,63 @@
selectedKindSub = subscribe({ kinds: [Number(kind)], authors: [currentPubkey], limit: 500 }, { closeOnEose: false, cacheUsage: 'CACHE_FIRST' });
}
function clearAllEventsFetchTimeout() {
if (allEventsFetchTimeoutId) {
clearTimeout(allEventsFetchTimeoutId);
allEventsFetchTimeoutId = null;
}
}
function closeAllEventsFetchSubscription() {
try {
if (allEventsFetchSub && typeof allEventsFetchSub.close === 'function') allEventsFetchSub.close();
} catch (_error) { }
allEventsFetchSub = null;
}
function finalizeGetAllEventsFetch(statusText) {
clearAllEventsFetchTimeout();
closeAllEventsFetchSubscription();
allEventsFetchInFlight = false;
if (btnGetAllEvents) btnGetAllEvents.disabled = false;
if (statusText) {
divKindsStatus.textContent = statusText;
} else {
const { kindsCount, totalEvents } = getCachedKindsAndEventTotals();
divKindsStatus.textContent = `${kindsCount} kinds · ${totalEvents.toLocaleString()} cached events`;
}
}
async function runOneShotGetAllEventsFetch() {
if (allEventsFetchInFlight) return;
if (!currentPubkey) {
divKindsStatus.textContent = 'Get all events failed: user pubkey unavailable';
return;
}
allEventsFetchInFlight = true;
if (btnGetAllEvents) btnGetAllEvents.disabled = true;
const beforeTotals = getCachedKindsAndEventTotals();
divKindsStatus.textContent = `Getting all events from relays for ${shortHex(currentPubkey)}...`;
try {
closeAllEventsFetchSubscription();
allEventsFetchSub = subscribe(
{ authors: [currentPubkey] },
{ closeOnEose: true, cacheUsage: 'CACHE_FIRST' }
);
allEventsFetchTimeoutId = setTimeout(() => {
const afterTotals = getCachedKindsAndEventTotals();
const added = Math.max(0, afterTotals.totalEvents - beforeTotals.totalEvents);
finalizeGetAllEventsFetch(`Get all events timed out · +${added.toLocaleString()} events (${afterTotals.totalEvents.toLocaleString()} total)`);
}, 30000);
} catch (error) {
finalizeGetAllEventsFetch(`Get all events failed: ${error?.message || String(error)}`);
}
}
async function selectKind(kind, options = {}) {
const { autoRefresh = true } = options;
@@ -1992,6 +2060,8 @@
});
logoutButton?.addEventListener('click', async () => {
closeAllEventsFetchSubscription();
clearAllEventsFetchTimeout();
try { await logout(); } catch (error) { console.error('[event-management.html] logout failed', error); }
});
@@ -2004,6 +2074,10 @@
}
});
btnGetAllEvents?.addEventListener('click', async () => {
await runOneShotGetAllEventsFetch();
});
inpEventSearch.addEventListener('input', renderEvents);
chkShowSuperseded?.addEventListener('change', renderEvents);
@@ -2319,6 +2393,15 @@
renderEvents();
});
window.addEventListener('ndkEose', (event) => {
if (!allEventsFetchInFlight || !allEventsFetchSub?.subId) return;
const eoseSubId = String(event?.detail?.subId || '');
if (!eoseSubId || eoseSubId !== String(allEventsFetchSub.subId)) return;
const { kindsCount, totalEvents } = getCachedKindsAndEventTotals();
finalizeGetAllEventsFetch(`Get all events complete · ${kindsCount} kinds · ${totalEvents.toLocaleString()} cached events`);
});
window.addEventListener('ndkEvent', (event) => {
const evt = event?.detail;
if (!evt || !Number.isFinite(Number(evt.kind)) || !evt.id) return;

View File

@@ -1,5 +1,5 @@
{
"VERSION": "v0.7.17",
"VERSION_NUMBER": "0.7.17",
"BUILD_DATE": "2026-06-09T19:15:08.620Z"
"VERSION": "v0.7.18",
"VERSION_NUMBER": "0.7.18",
"BUILD_DATE": "2026-06-14T12:07:35.533Z"
}

View File

@@ -1179,6 +1179,7 @@ function startRelayHealthCheck() {
class MessageBasedSigner {
constructor() {
this.pubkey = null;
this.signedEventsBySig = new Map(); // sig -> signed raw event from signer page
}
async user() {
@@ -1196,12 +1197,59 @@ class MessageBasedSigner {
async sign(event) {
console.log('[Worker] MessageBasedSigner: Signing event kind:', event.kind);
// Request signing from page
const signedEvent = await this.requestFromPage('signEvent', { event });
const sig = String(signedEvent?.sig || '').trim();
if (!sig) {
throw new Error('signEvent returned no signature');
}
// Keep a snapshot so caller can align NDKEvent fields to exactly what was signed.
const snapshot = {
id: String(signedEvent?.id || ''),
pubkey: String(signedEvent?.pubkey || ''),
created_at: Number(signedEvent?.created_at || 0),
kind: Number(signedEvent?.kind),
content: String(signedEvent?.content || ''),
tags: Array.isArray(signedEvent?.tags)
? signedEvent.tags.map((tag) => Array.isArray(tag) ? tag.map((v) => String(v ?? '')) : [String(tag ?? '')])
: [],
sig
};
this.signedEventsBySig.set(sig, snapshot);
if (this.signedEventsBySig.size > 200) {
const oldestKey = this.signedEventsBySig.keys().next().value;
if (oldestKey) this.signedEventsBySig.delete(oldestKey);
}
console.log('[Worker] MessageBasedSigner: Event signed');
return signedEvent.sig;
return sig;
}
applySignedSnapshotToEvent(ndkEvent) {
if (!ndkEvent) return false;
const sig = String(ndkEvent?.sig || '').trim();
if (!sig) return false;
const snapshot = this.signedEventsBySig.get(sig);
if (!snapshot) return false;
this.signedEventsBySig.delete(sig);
if (snapshot.id) ndkEvent.id = snapshot.id;
if (snapshot.pubkey) ndkEvent.pubkey = snapshot.pubkey;
if (Number.isFinite(snapshot.created_at) && snapshot.created_at > 0) ndkEvent.created_at = snapshot.created_at;
if (Number.isFinite(snapshot.kind)) ndkEvent.kind = snapshot.kind;
ndkEvent.content = String(snapshot.content || '');
ndkEvent.tags = Array.isArray(snapshot.tags)
? snapshot.tags.map((tag) => Array.isArray(tag) ? tag.map((v) => String(v ?? '')) : [String(tag ?? '')])
: [];
ndkEvent.sig = snapshot.sig;
return true;
}
async encrypt(recipient, plaintext) {
@@ -1307,6 +1355,16 @@ class MessageBasedSigner {
// Create message-based signer instance
let messageSigner = null;
async function signEventWithMessageSigner(event, opts) {
if (!event) throw new Error('Missing event to sign');
await event.sign(messageSigner, opts);
try {
messageSigner?.applySignedSnapshotToEvent?.(event);
} catch (error) {
console.warn('[Worker] Failed applying signed snapshot to event:', error?.message || error);
}
}
// Initialize NDK with Dexie cache and message-based signer
async function initNDK() {
console.log('[Worker] Initializing NDK...');
@@ -2662,7 +2720,7 @@ async function publishDirectProofs(debugContext = null) {
});
log('sign 7375 start', { mintUrl });
await tokenEvent.sign(messageSigner);
await signEventWithMessageSigner(tokenEvent);
log('sign 7375 done', { mintUrl });
log('publish 7375 start', { mintUrl });
@@ -2687,7 +2745,7 @@ async function publishDirectProofs(debugContext = null) {
content: '',
created_at: Math.floor(Date.now() / 1000)
});
await deletionEvent.sign(messageSigner);
await signEventWithMessageSigner(deletionEvent);
await deletionEvent.publish();
}
log('delete old 7375 done');
@@ -2726,7 +2784,7 @@ async function publishDirectProofs(debugContext = null) {
});
log('sign 17375 start');
await walletEvent.sign(messageSigner);
await signEventWithMessageSigner(walletEvent);
log('sign 17375 done');
log('publish 17375 start');
@@ -2750,7 +2808,7 @@ async function publishDirectProofs(debugContext = null) {
content: '',
created_at: Math.floor(Date.now() / 1000)
});
await deletionEvent.sign(messageSigner);
await signEventWithMessageSigner(deletionEvent);
await deletionEvent.publish();
}
log('delete old 17375 done');
@@ -2951,7 +3009,7 @@ async function publishSpendingHistoryEvent(entry = {}) {
created_at: Math.floor(Date.now() / 1000)
});
await historyEvent.sign(messageSigner);
await signEventWithMessageSigner(historyEvent);
const relaySet = await historyEvent.publish();
if (relaySet && relaySet.size > 0) {
for (const relay of relaySet) {
@@ -3003,7 +3061,7 @@ async function handleWalletPublishMintList(requestId, relays, receiveMints, port
mintListEvent.mints = mints;
mintListEvent.relays = relayUrls;
if (p2pk) mintListEvent.p2pk = p2pk;
await mintListEvent.sign(messageSigner);
await signEventWithMessageSigner(mintListEvent);
const relaySet = await mintListEvent.publishReplaceable();
eventId = mintListEvent.id || null;
publishedRelayCount = relaySet ? relaySet.size : 0;
@@ -3019,7 +3077,7 @@ async function handleWalletPublishMintList(requestId, relays, receiveMints, port
content: '',
created_at: Math.floor(Date.now() / 1000)
});
await mintListEvent.sign(messageSigner);
await signEventWithMessageSigner(mintListEvent);
const relaySet = await mintListEvent.publish();
eventId = mintListEvent.id || null;
publishedRelayCount = relaySet ? relaySet.size : 0;
@@ -3233,7 +3291,7 @@ async function handleWalletSendNutzap(
created_at: Math.floor(Date.now() / 1000)
});
await nutzapEvent.sign(messageSigner);
await signEventWithMessageSigner(nutzapEvent);
let relaySet = null;
if (recipientRelayUrls.length > 0 && NDKRelaySet?.fromRelayUrls) {
@@ -4256,7 +4314,7 @@ async function publishUserSettingsNow() {
created_at: now
});
await ndkEvent.sign(messageSigner);
await signEventWithMessageSigner(ndkEvent);
const relaySet = await ndkEvent.publish();
if (relaySet && relaySet.size > 0) {
for (const relay of relaySet) {
@@ -5304,7 +5362,7 @@ async function handlePublish(requestId, event, port) {
// Sign using message-based signer (will request signing from page)
console.log('[Worker] Requesting signature from page...');
await ndkEvent.sign(messageSigner);
await signEventWithMessageSigner(ndkEvent);
console.log('[Worker] Event signed, publishing to relays...');
// Publish to relays