Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
456f0e1f2e | ||
|
|
4abb4bf33c |
@@ -915,16 +915,56 @@ export function formatTimeAgo(timestamp) {
|
||||
}
|
||||
|
||||
// Store timestamps for real-time updates
|
||||
const timeAgoElements = new Map(); // element -> timestamp
|
||||
const timeAgoElements = new Map(); // element -> { timestamp, eventId }
|
||||
|
||||
// Track relay publish counts per event ID from broadcastProgress events.
|
||||
// Keyed by event ID → number of successful relay publishes.
|
||||
const relayCountByEventId = new Map();
|
||||
|
||||
// Listen for broadcast progress events to capture final relay counts.
|
||||
// The worker emits ndkBroadcastProgress with phase 'done' containing the
|
||||
// total successful count. We store it so post cards can show "21r - 1h".
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('ndkBroadcastProgress', (event) => {
|
||||
const d = event.detail;
|
||||
if (!d || d.phase !== 'done') return;
|
||||
const eventId = d.eventId;
|
||||
if (!eventId) return;
|
||||
relayCountByEventId.set(eventId, d.successful || 0);
|
||||
// Update any already-rendered time elements for this event.
|
||||
timeAgoElements.forEach((info, element) => {
|
||||
if (info.eventId === eventId && element.isConnected) {
|
||||
element.textContent = formatTimeAgoWithRelays(info.timestamp, info.eventId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time ago with optional relay count prefix.
|
||||
* Shows "21r - 1h" when a relay count is available, else just "1h".
|
||||
* @param {number} timestamp - Unix timestamp in seconds
|
||||
* @param {string} [eventId] - Optional event ID to look up relay count
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatTimeAgoWithRelays(timestamp, eventId) {
|
||||
const timeStr = formatTimeAgo(timestamp);
|
||||
if (eventId && relayCountByEventId.has(eventId)) {
|
||||
const count = relayCountByEventId.get(eventId);
|
||||
return `${count}r - ${timeStr}`;
|
||||
}
|
||||
return timeStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an element for time updates
|
||||
* @param {HTMLElement} element - The element to update
|
||||
* @param {number} timestamp - Unix timestamp in seconds
|
||||
* @param {string} [eventId] - Optional event ID for relay count display
|
||||
*/
|
||||
export function registerTimeAgo(element, timestamp) {
|
||||
timeAgoElements.set(element, timestamp);
|
||||
element.textContent = formatTimeAgo(timestamp);
|
||||
export function registerTimeAgo(element, timestamp, eventId) {
|
||||
timeAgoElements.set(element, { timestamp, eventId });
|
||||
element.textContent = formatTimeAgoWithRelays(timestamp, eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -932,9 +972,9 @@ export function registerTimeAgo(element, timestamp) {
|
||||
* Call this periodically (e.g., every 30 seconds)
|
||||
*/
|
||||
export function updateTimeAgos() {
|
||||
timeAgoElements.forEach((timestamp, element) => {
|
||||
timeAgoElements.forEach((info, element) => {
|
||||
if (element.isConnected) {
|
||||
element.textContent = formatTimeAgo(timestamp);
|
||||
element.textContent = formatTimeAgoWithRelays(info.timestamp, info.eventId);
|
||||
} else {
|
||||
// Clean up detached elements
|
||||
timeAgoElements.delete(element);
|
||||
@@ -1085,10 +1125,12 @@ export function renderFooterRow(eventId, eventData, options = {}) {
|
||||
container.appendChild(nutzapItem);
|
||||
container.appendChild(midControls);
|
||||
|
||||
// Time as the final item in the same row
|
||||
// Time as the final item in the same row.
|
||||
// Pass eventId so registerTimeAgo can prepend the relay count
|
||||
// (e.g., "21r - 1h") when a broadcastProgress 'done' event arrives.
|
||||
const timeEl = document.createElement('span');
|
||||
timeEl.className = 'divPostTime';
|
||||
registerTimeAgo(timeEl, eventData.created_at);
|
||||
registerTimeAgo(timeEl, eventData.created_at, eventId);
|
||||
container.appendChild(timeEl);
|
||||
|
||||
footerRow.appendChild(container);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"VERSION": "v0.7.68",
|
||||
"VERSION_NUMBER": "0.7.68",
|
||||
"BUILD_DATE": "2026-06-30T13:15:02.809Z"
|
||||
"VERSION": "v0.7.70",
|
||||
"VERSION_NUMBER": "0.7.70",
|
||||
"BUILD_DATE": "2026-06-30T13:28:38.113Z"
|
||||
}
|
||||
|
||||
+12
-7
@@ -4616,8 +4616,13 @@ async function handleWalletPayInvoice(requestId, invoice, mint, port) {
|
||||
const meltQuote = await wallet.createMeltQuote(pr);
|
||||
const quoteAmount = Number(meltQuote?.amount || 0);
|
||||
const quoteFeeReserve = Number(meltQuote?.fee_reserve || 0);
|
||||
// The mint's quoted fee_reserve is an estimate; the actual melt fee can be
|
||||
// slightly higher, causing "not enough inputs provided for melt" errors.
|
||||
// Add a safety buffer — any overpayment is returned as change proofs.
|
||||
const FEE_RESERVE_BUFFER_SATS = 10;
|
||||
const required = quoteAmount + quoteFeeReserve;
|
||||
log('mint attempt:quote', { mintUrl, quoteAmount, quoteFeeReserve, required });
|
||||
const sendAmount = required + FEE_RESERVE_BUFFER_SATS;
|
||||
log('mint attempt:quote', { mintUrl, quoteAmount, quoteFeeReserve, required, sendAmount });
|
||||
|
||||
if (!Number.isFinite(required) || required <= 0) {
|
||||
log('mint attempt:invalid-quote', { mintUrl, required });
|
||||
@@ -4625,28 +4630,28 @@ async function handleWalletPayInvoice(requestId, invoice, mint, port) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (proofBalance < required) {
|
||||
log('mint attempt:insufficient', { mintUrl, proofBalance, required });
|
||||
if (proofBalance < sendAmount) {
|
||||
log('mint attempt:insufficient', { mintUrl, proofBalance, sendAmount });
|
||||
attemptErrors.push({ mint: mintUrl, message: `Insufficient funds on mint (${proofBalance} sats)` });
|
||||
continue;
|
||||
}
|
||||
|
||||
const sendResult = await wallet.send(required, proofs);
|
||||
const sendResult = await wallet.send(sendAmount, proofs);
|
||||
const keep = Array.isArray(sendResult?.keep) ? sendResult.keep : [];
|
||||
const sendProofs = Array.isArray(sendResult?.send) ? sendResult.send : [];
|
||||
log('mint attempt:send-split', { mintUrl, keepCount: keep.length, sendCount: sendProofs.length });
|
||||
log('mint attempt:send-split', { mintUrl, keepCount: keep.length, sendCount: sendProofs.length, sendAmount });
|
||||
|
||||
const meltResult = await wallet.meltProofs(meltQuote, sendProofs);
|
||||
const change = Array.isArray(meltResult?.change) ? meltResult.change : [];
|
||||
upsertMintProofs(mintUrl, [...keep, ...change]);
|
||||
|
||||
const changeAmount = getProofTotal(change);
|
||||
const spentAmount = Math.max(0, required - changeAmount);
|
||||
const spentAmount = Math.max(0, sendAmount - changeAmount);
|
||||
paidAmountSats = Number.isFinite(quoteAmount) && quoteAmount > 0
|
||||
? quoteAmount
|
||||
: spentAmount;
|
||||
|
||||
log('mint attempt:success', { mintUrl, paidAmountSats, changeAmount, preimage: meltResult?.preimage || null });
|
||||
log('mint attempt:success', { mintUrl, paidAmountSats, changeAmount, spentAmount, preimage: meltResult?.preimage || null });
|
||||
|
||||
usedMint = mintUrl;
|
||||
payResult = meltResult;
|
||||
|
||||
Reference in New Issue
Block a user