mirror of
https://github.com/dergigi/boris.git
synced 2025-12-18 15:14:20 +01:00
refactor: remove all console.log debug output
This commit is contained in:
@@ -215,7 +215,6 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||||||
|
|
||||||
const debugEnabled = req.query.debug === '1' || req.headers['x-boris-debug'] === '1'
|
const debugEnabled = req.query.debug === '1' || req.headers['x-boris-debug'] === '1'
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
console.log('[article-og] request', JSON.stringify({
|
|
||||||
naddr,
|
naddr,
|
||||||
ua: userAgent || null,
|
ua: userAgent || null,
|
||||||
isCrawlerRequest,
|
isCrawlerRequest,
|
||||||
@@ -257,7 +256,6 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
console.log('[article-og] response', JSON.stringify({ mode: 'browser', naddr }))
|
|
||||||
}
|
}
|
||||||
return res.status(200).send(html)
|
return res.status(200).send(html)
|
||||||
}
|
}
|
||||||
@@ -268,7 +266,6 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||||||
if (cached && cached.expires > now) {
|
if (cached && cached.expires > now) {
|
||||||
setCacheHeaders(res)
|
setCacheHeaders(res)
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
console.log('[article-og] response', JSON.stringify({ mode: 'bot', naddr, cache: true }))
|
|
||||||
}
|
}
|
||||||
return res.status(200).send(cached.html)
|
return res.status(200).send(cached.html)
|
||||||
}
|
}
|
||||||
@@ -286,7 +283,6 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||||||
// Send response
|
// Send response
|
||||||
setCacheHeaders(res)
|
setCacheHeaders(res)
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
console.log('[article-og] response', JSON.stringify({ mode: 'bot', naddr, cache: false }))
|
|
||||||
}
|
}
|
||||||
return res.status(200).send(html)
|
return res.status(200).send(html)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -296,7 +292,6 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|||||||
const html = generateHtml(naddr, null)
|
const html = generateHtml(naddr, null)
|
||||||
setCacheHeaders(res, 3600)
|
setCacheHeaders(res, 3600)
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
console.log('[article-og] response', JSON.stringify({ mode: 'bot-fallback', naddr }))
|
|
||||||
}
|
}
|
||||||
return res.status(200).send(html)
|
return res.status(200).send(html)
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/App.tsx
60
src/App.tsx
@@ -70,18 +70,14 @@ function AppRoutes({
|
|||||||
|
|
||||||
// Subscribe to contacts controller
|
// Subscribe to contacts controller
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[contacts] 🎧 Subscribing to contacts controller')
|
|
||||||
const unsubContacts = contactsController.onContacts((contacts) => {
|
const unsubContacts = contactsController.onContacts((contacts) => {
|
||||||
console.log('[contacts] 📥 Received contacts:', contacts.size)
|
|
||||||
setContacts(contacts)
|
setContacts(contacts)
|
||||||
})
|
})
|
||||||
const unsubLoading = contactsController.onLoading((loading) => {
|
const unsubLoading = contactsController.onLoading((loading) => {
|
||||||
console.log('[contacts] 📥 Loading state:', loading)
|
|
||||||
setContactsLoading(loading)
|
setContactsLoading(loading)
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
console.log('[contacts] 🔇 Unsubscribing from contacts controller')
|
|
||||||
unsubContacts()
|
unsubContacts()
|
||||||
unsubLoading()
|
unsubLoading()
|
||||||
}
|
}
|
||||||
@@ -100,25 +96,21 @@ function AppRoutes({
|
|||||||
|
|
||||||
// Load contacts
|
// Load contacts
|
||||||
if (pubkey && contacts.size === 0 && !contactsLoading) {
|
if (pubkey && contacts.size === 0 && !contactsLoading) {
|
||||||
console.log('[contacts] 🚀 Auto-loading contacts on mount/login')
|
|
||||||
contactsController.start({ relayPool, pubkey })
|
contactsController.start({ relayPool, pubkey })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load highlights (controller manages its own state)
|
// Load highlights (controller manages its own state)
|
||||||
if (pubkey && eventStore && !highlightsController.isLoadedFor(pubkey)) {
|
if (pubkey && eventStore && !highlightsController.isLoadedFor(pubkey)) {
|
||||||
console.log('[highlights] 🚀 Auto-loading highlights on mount/login')
|
|
||||||
highlightsController.start({ relayPool, eventStore, pubkey })
|
highlightsController.start({ relayPool, eventStore, pubkey })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load writings (controller manages its own state)
|
// Load writings (controller manages its own state)
|
||||||
if (pubkey && eventStore && !writingsController.isLoadedFor(pubkey)) {
|
if (pubkey && eventStore && !writingsController.isLoadedFor(pubkey)) {
|
||||||
console.log('[writings] 🚀 Auto-loading writings on mount/login')
|
|
||||||
writingsController.start({ relayPool, eventStore, pubkey })
|
writingsController.start({ relayPool, eventStore, pubkey })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load reading progress (controller manages its own state)
|
// Load reading progress (controller manages its own state)
|
||||||
if (pubkey && eventStore && !readingProgressController.isLoadedFor(pubkey)) {
|
if (pubkey && eventStore && !readingProgressController.isLoadedFor(pubkey)) {
|
||||||
console.log('[progress] 🚀 Auto-loading reading progress on mount/login')
|
|
||||||
readingProgressController.start({ relayPool, eventStore, pubkey })
|
readingProgressController.start({ relayPool, eventStore, pubkey })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,40 +378,30 @@ function App() {
|
|||||||
// Return an already-resolved promise so upstream await finishes immediately
|
// Return an already-resolved promise so upstream await finishes immediately
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
console.log('[bunker] ✅ Wired NostrConnectSigner to RelayPool publish/subscription (before account load)')
|
|
||||||
|
|
||||||
// Create a relay group for better event deduplication and management
|
// Create a relay group for better event deduplication and management
|
||||||
pool.group(RELAYS)
|
pool.group(RELAYS)
|
||||||
console.log('[bunker] Created relay group with', RELAYS.length, 'relays (including local)')
|
|
||||||
|
|
||||||
// Load persisted accounts from localStorage
|
// Load persisted accounts from localStorage
|
||||||
try {
|
try {
|
||||||
const accountsJson = localStorage.getItem('accounts')
|
const accountsJson = localStorage.getItem('accounts')
|
||||||
console.log('[bunker] Raw accounts from localStorage:', accountsJson)
|
|
||||||
|
|
||||||
const json = JSON.parse(accountsJson || '[]')
|
const json = JSON.parse(accountsJson || '[]')
|
||||||
console.log('[bunker] Parsed accounts:', json.length, 'accounts')
|
|
||||||
|
|
||||||
await accounts.fromJSON(json)
|
await accounts.fromJSON(json)
|
||||||
console.log('[bunker] Loaded', accounts.accounts.length, 'accounts from storage')
|
|
||||||
console.log('[bunker] Account types:', accounts.accounts.map(a => ({ id: a.id, type: a.type })))
|
|
||||||
|
|
||||||
// Load active account from storage
|
// Load active account from storage
|
||||||
const activeId = localStorage.getItem('active')
|
const activeId = localStorage.getItem('active')
|
||||||
console.log('[bunker] Active ID from localStorage:', activeId)
|
|
||||||
|
|
||||||
if (activeId) {
|
if (activeId) {
|
||||||
const account = accounts.getAccount(activeId)
|
const account = accounts.getAccount(activeId)
|
||||||
console.log('[bunker] Found account for ID?', !!account, account?.type)
|
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
accounts.setActive(activeId)
|
accounts.setActive(activeId)
|
||||||
console.log('[bunker] ✅ Restored active account:', activeId, 'type:', account.type)
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('[bunker] ⚠️ Active ID found but account not in list')
|
console.warn('[bunker] ⚠️ Active ID found but account not in list')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('[bunker] No active account ID in localStorage')
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[bunker] ❌ Failed to load accounts from storage:', err)
|
console.error('[bunker] ❌ Failed to load accounts from storage:', err)
|
||||||
@@ -444,11 +426,6 @@ function App() {
|
|||||||
const reconnectedAccounts = new Set<string>()
|
const reconnectedAccounts = new Set<string>()
|
||||||
|
|
||||||
const bunkerReconnectSub = accounts.active$.subscribe(async (account) => {
|
const bunkerReconnectSub = accounts.active$.subscribe(async (account) => {
|
||||||
console.log('[bunker] Active account changed:', {
|
|
||||||
hasAccount: !!account,
|
|
||||||
type: account?.type,
|
|
||||||
id: account?.id
|
|
||||||
})
|
|
||||||
|
|
||||||
if (account && account.type === 'nostr-connect') {
|
if (account && account.type === 'nostr-connect') {
|
||||||
const nostrConnectAccount = account as Accounts.NostrConnectAccount<unknown>
|
const nostrConnectAccount = account as Accounts.NostrConnectAccount<unknown>
|
||||||
@@ -456,23 +433,15 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
if (!(nostrConnectAccount as unknown as { disableQueue?: boolean }).disableQueue) {
|
if (!(nostrConnectAccount as unknown as { disableQueue?: boolean }).disableQueue) {
|
||||||
(nostrConnectAccount as unknown as { disableQueue?: boolean }).disableQueue = true
|
(nostrConnectAccount as unknown as { disableQueue?: boolean }).disableQueue = true
|
||||||
console.log('[bunker] ⚙️ Disabled account request queueing for nostr-connect')
|
|
||||||
}
|
}
|
||||||
} catch (err) { console.warn('[bunker] failed to disable queue', err) }
|
} catch (err) { console.warn('[bunker] failed to disable queue', err) }
|
||||||
// Note: for Amber bunker, the remote signer pubkey is the user's pubkey. This is expected.
|
// Note: for Amber bunker, the remote signer pubkey is the user's pubkey. This is expected.
|
||||||
|
|
||||||
// Skip if we've already reconnected this account
|
// Skip if we've already reconnected this account
|
||||||
if (reconnectedAccounts.has(account.id)) {
|
if (reconnectedAccounts.has(account.id)) {
|
||||||
console.log('[bunker] ⏭️ Already reconnected this account, skipping')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[bunker] Account detected. Status:', {
|
|
||||||
listening: nostrConnectAccount.signer.listening,
|
|
||||||
isConnected: nostrConnectAccount.signer.isConnected,
|
|
||||||
hasRemote: !!nostrConnectAccount.signer.remote,
|
|
||||||
bunkerRelays: nostrConnectAccount.signer.relays
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// For restored signers, ensure they have the pool's subscription methods
|
// For restored signers, ensure they have the pool's subscription methods
|
||||||
@@ -486,10 +455,8 @@ function App() {
|
|||||||
const newBunkerRelays = bunkerRelays.filter(url => !existingRelayUrls.has(url))
|
const newBunkerRelays = bunkerRelays.filter(url => !existingRelayUrls.has(url))
|
||||||
|
|
||||||
if (newBunkerRelays.length > 0) {
|
if (newBunkerRelays.length > 0) {
|
||||||
console.log('[bunker] Adding bunker relays to pool BEFORE signer recreation:', newBunkerRelays)
|
|
||||||
pool.group(newBunkerRelays)
|
pool.group(newBunkerRelays)
|
||||||
} else {
|
} else {
|
||||||
console.log('[bunker] Bunker relays already in pool')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const recreatedSigner = new NostrConnectSigner({
|
const recreatedSigner = new NostrConnectSigner({
|
||||||
@@ -503,13 +470,11 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
const mergedRelays = Array.from(new Set([...(signerData.relays || []), ...RELAYS]))
|
const mergedRelays = Array.from(new Set([...(signerData.relays || []), ...RELAYS]))
|
||||||
recreatedSigner.relays = mergedRelays
|
recreatedSigner.relays = mergedRelays
|
||||||
console.log('[bunker] 🔗 Signer relays merged with app RELAYS:', mergedRelays)
|
|
||||||
} catch (err) { console.warn('[bunker] failed to merge signer relays', err) }
|
} catch (err) { console.warn('[bunker] failed to merge signer relays', err) }
|
||||||
|
|
||||||
// Replace the signer on the account
|
// Replace the signer on the account
|
||||||
nostrConnectAccount.signer = recreatedSigner
|
nostrConnectAccount.signer = recreatedSigner
|
||||||
console.log('[bunker] ✅ Signer recreated with pool context')
|
|
||||||
|
|
||||||
// Debug: log publish/subscription calls made by signer (decrypt/sign requests)
|
// Debug: log publish/subscription calls made by signer (decrypt/sign requests)
|
||||||
// IMPORTANT: bind originals to preserve `this` context used internally by the signer
|
// IMPORTANT: bind originals to preserve `this` context used internally by the signer
|
||||||
const originalPublish = (recreatedSigner as unknown as { publishMethod: (relays: string[], event: unknown) => unknown }).publishMethod.bind(recreatedSigner)
|
const originalPublish = (recreatedSigner as unknown as { publishMethod: (relays: string[], event: unknown) => unknown }).publishMethod.bind(recreatedSigner)
|
||||||
@@ -531,7 +496,6 @@ function App() {
|
|||||||
tags: (event as { tags?: unknown })?.tags,
|
tags: (event as { tags?: unknown })?.tags,
|
||||||
contentLength: typeof content === 'string' ? content.length : undefined
|
contentLength: typeof content === 'string' ? content.length : undefined
|
||||||
}
|
}
|
||||||
console.log('[bunker] publish via signer:', summary)
|
|
||||||
try { DebugBus.info('bunker', 'publish', summary) } catch (err) { console.warn('[bunker] failed to log to DebugBus', err) }
|
try { DebugBus.info('bunker', 'publish', summary) } catch (err) { console.warn('[bunker] failed to log to DebugBus', err) }
|
||||||
} catch (err) { console.warn('[bunker] failed to log publish summary', err) }
|
} catch (err) { console.warn('[bunker] failed to log publish summary', err) }
|
||||||
// Fire-and-forget publish: trigger the publish but do not return the
|
// Fire-and-forget publish: trigger the publish but do not return the
|
||||||
@@ -549,7 +513,6 @@ function App() {
|
|||||||
const originalSubscribe = (recreatedSigner as unknown as { subscriptionMethod: (relays: string[], filters: unknown[]) => unknown }).subscriptionMethod.bind(recreatedSigner)
|
const originalSubscribe = (recreatedSigner as unknown as { subscriptionMethod: (relays: string[], filters: unknown[]) => unknown }).subscriptionMethod.bind(recreatedSigner)
|
||||||
;(recreatedSigner as unknown as { subscriptionMethod: (relays: string[], filters: unknown[]) => unknown }).subscriptionMethod = (relays: string[], filters: unknown[]) => {
|
;(recreatedSigner as unknown as { subscriptionMethod: (relays: string[], filters: unknown[]) => unknown }).subscriptionMethod = (relays: string[], filters: unknown[]) => {
|
||||||
try {
|
try {
|
||||||
console.log('[bunker] subscribe via signer:', { relays, filters })
|
|
||||||
try { DebugBus.info('bunker', 'subscribe', { relays, filters }) } catch (err) { console.warn('[bunker] failed to log subscribe to DebugBus', err) }
|
try { DebugBus.info('bunker', 'subscribe', { relays, filters }) } catch (err) { console.warn('[bunker] failed to log subscribe to DebugBus', err) }
|
||||||
} catch (err) { console.warn('[bunker] failed to log subscribe summary', err) }
|
} catch (err) { console.warn('[bunker] failed to log subscribe summary', err) }
|
||||||
return originalSubscribe(relays, filters)
|
return originalSubscribe(relays, filters)
|
||||||
@@ -559,20 +522,15 @@ function App() {
|
|||||||
// Just ensure the signer is listening for responses - don't call connect() again
|
// Just ensure the signer is listening for responses - don't call connect() again
|
||||||
// The fromBunkerURI already connected with permissions during login
|
// The fromBunkerURI already connected with permissions during login
|
||||||
if (!nostrConnectAccount.signer.listening) {
|
if (!nostrConnectAccount.signer.listening) {
|
||||||
console.log('[bunker] Opening signer subscription...')
|
|
||||||
await nostrConnectAccount.signer.open()
|
await nostrConnectAccount.signer.open()
|
||||||
console.log('[bunker] ✅ Signer subscription opened')
|
|
||||||
} else {
|
} else {
|
||||||
console.log('[bunker] ✅ Signer already listening')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt a guarded reconnect to ensure Amber authorizes decrypt operations
|
// Attempt a guarded reconnect to ensure Amber authorizes decrypt operations
|
||||||
try {
|
try {
|
||||||
if (nostrConnectAccount.signer.remote && !reconnectedAccounts.has(account.id)) {
|
if (nostrConnectAccount.signer.remote && !reconnectedAccounts.has(account.id)) {
|
||||||
const permissions = getDefaultBunkerPermissions()
|
const permissions = getDefaultBunkerPermissions()
|
||||||
console.log('[bunker] Attempting guarded connect() with permissions to ensure decrypt perms', { count: permissions.length })
|
|
||||||
await nostrConnectAccount.signer.connect(undefined, permissions)
|
await nostrConnectAccount.signer.connect(undefined, permissions)
|
||||||
console.log('[bunker] ✅ Guarded connect() succeeded with permissions')
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[bunker] ⚠️ Guarded connect() failed:', e)
|
console.warn('[bunker] ⚠️ Guarded connect() failed:', e)
|
||||||
@@ -581,7 +539,6 @@ function App() {
|
|||||||
// Give the subscription a moment to fully establish before allowing decrypt operations
|
// Give the subscription a moment to fully establish before allowing decrypt operations
|
||||||
// This ensures the signer is ready to handle and receive responses
|
// This ensures the signer is ready to handle and receive responses
|
||||||
await new Promise(resolve => setTimeout(resolve, 100))
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
console.log("[bunker] Subscription ready after startup delay")
|
|
||||||
// Fire-and-forget: probe decrypt path to verify Amber responds to NIP-46 decrypt
|
// Fire-and-forget: probe decrypt path to verify Amber responds to NIP-46 decrypt
|
||||||
try {
|
try {
|
||||||
const withTimeout = async <T,>(p: Promise<T>, ms = 10000): Promise<T> => {
|
const withTimeout = async <T,>(p: Promise<T>, ms = 10000): Promise<T> => {
|
||||||
@@ -594,38 +551,24 @@ function App() {
|
|||||||
const self = nostrConnectAccount.pubkey
|
const self = nostrConnectAccount.pubkey
|
||||||
// Try a roundtrip so the bunker can respond successfully
|
// Try a roundtrip so the bunker can respond successfully
|
||||||
try {
|
try {
|
||||||
console.log('[bunker] 🔎 Probe nip44 roundtrip (encrypt→decrypt)…')
|
|
||||||
const cipher44 = await withTimeout(nostrConnectAccount.signer.nip44!.encrypt(self, 'probe-nip44'))
|
const cipher44 = await withTimeout(nostrConnectAccount.signer.nip44!.encrypt(self, 'probe-nip44'))
|
||||||
const plain44 = await withTimeout(nostrConnectAccount.signer.nip44!.decrypt(self, cipher44))
|
const plain44 = await withTimeout(nostrConnectAccount.signer.nip44!.decrypt(self, cipher44))
|
||||||
console.log('[bunker] 🔎 Probe nip44 responded:', typeof plain44 === 'string' ? plain44 : typeof plain44)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('[bunker] 🔎 Probe nip44 result:', err instanceof Error ? err.message : err)
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
console.log('[bunker] 🔎 Probe nip04 roundtrip (encrypt→decrypt)…')
|
|
||||||
const cipher04 = await withTimeout(nostrConnectAccount.signer.nip04!.encrypt(self, 'probe-nip04'))
|
const cipher04 = await withTimeout(nostrConnectAccount.signer.nip04!.encrypt(self, 'probe-nip04'))
|
||||||
const plain04 = await withTimeout(nostrConnectAccount.signer.nip04!.decrypt(self, cipher04))
|
const plain04 = await withTimeout(nostrConnectAccount.signer.nip04!.decrypt(self, cipher04))
|
||||||
console.log('[bunker] 🔎 Probe nip04 responded:', typeof plain04 === 'string' ? plain04 : typeof plain04)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('[bunker] 🔎 Probe nip04 result:', err instanceof Error ? err.message : err)
|
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('[bunker] 🔎 Probe setup failed:', err)
|
|
||||||
}
|
}
|
||||||
// The bunker remembers the permissions from the initial connection
|
// The bunker remembers the permissions from the initial connection
|
||||||
nostrConnectAccount.signer.isConnected = true
|
nostrConnectAccount.signer.isConnected = true
|
||||||
|
|
||||||
console.log('[bunker] Final signer status:', {
|
|
||||||
listening: nostrConnectAccount.signer.listening,
|
|
||||||
isConnected: nostrConnectAccount.signer.isConnected,
|
|
||||||
remote: nostrConnectAccount.signer.remote,
|
|
||||||
relays: nostrConnectAccount.signer.relays
|
|
||||||
})
|
|
||||||
|
|
||||||
// Mark this account as reconnected
|
// Mark this account as reconnected
|
||||||
reconnectedAccounts.add(account.id)
|
reconnectedAccounts.add(account.id)
|
||||||
console.log('[bunker] 🎉 Signer ready for signing')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[bunker] ❌ Failed to open signer:', error)
|
console.error('[bunker] ❌ Failed to open signer:', error)
|
||||||
}
|
}
|
||||||
@@ -639,7 +582,6 @@ function App() {
|
|||||||
next: () => {}, // No-op, we don't care about events
|
next: () => {}, // No-op, we don't care about events
|
||||||
error: (err) => console.warn('Keep-alive subscription error:', err)
|
error: (err) => console.warn('Keep-alive subscription error:', err)
|
||||||
})
|
})
|
||||||
console.log('🔗 Created keep-alive subscription for', RELAYS.length, 'relay(s)')
|
|
||||||
|
|
||||||
// Store subscription for cleanup
|
// Store subscription for cleanup
|
||||||
;(pool as unknown as { _keepAliveSubscription: typeof keepAliveSub })._keepAliveSubscription = keepAliveSub
|
;(pool as unknown as { _keepAliveSubscription: typeof keepAliveSub })._keepAliveSubscription = keepAliveSub
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ const BlogPostCard: React.FC<BlogPostCardProps> = ({ post, href, level, readingP
|
|||||||
|
|
||||||
// Debug log
|
// Debug log
|
||||||
if (readingProgress !== undefined) {
|
if (readingProgress !== undefined) {
|
||||||
console.log('[progress] 🎴 Card render:', post.title.slice(0, 30), '=> progress:', progressPercent + '%', 'color:', progressColor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -151,33 +151,18 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
// Callback to save reading position
|
// Callback to save reading position
|
||||||
const handleSavePosition = useCallback(async (position: number) => {
|
const handleSavePosition = useCallback(async (position: number) => {
|
||||||
if (!activeAccount || !relayPool || !eventStore || !articleIdentifier) {
|
if (!activeAccount || !relayPool || !eventStore || !articleIdentifier) {
|
||||||
console.log('[progress] ⏭️ ContentPanel: Skipping save - missing requirements:', {
|
|
||||||
hasAccount: !!activeAccount,
|
|
||||||
hasRelayPool: !!relayPool,
|
|
||||||
hasEventStore: !!eventStore,
|
|
||||||
hasIdentifier: !!articleIdentifier
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!settings?.syncReadingPosition) {
|
if (!settings?.syncReadingPosition) {
|
||||||
console.log('[progress] ⏭️ ContentPanel: Sync disabled in settings')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if content is long enough to track reading progress
|
// Check if content is long enough to track reading progress
|
||||||
if (!shouldTrackReadingProgress(html, markdown)) {
|
if (!shouldTrackReadingProgress(html, markdown)) {
|
||||||
console.log('[progress] ⏭️ ContentPanel: Content too short to track reading progress')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||||
console.log('[progress] 💾 ContentPanel: Saving position:', {
|
|
||||||
position,
|
|
||||||
percentage: Math.round(position * 100) + '%',
|
|
||||||
scrollTop,
|
|
||||||
articleIdentifier: articleIdentifier.slice(0, 50) + '...',
|
|
||||||
url: selectedUrl?.slice(0, 50)
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const factory = new EventFactory({ signer: activeAccount })
|
const factory = new EventFactory({ signer: activeAccount })
|
||||||
@@ -192,7 +177,6 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
scrollTop
|
scrollTop
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
console.log('[progress] ✅ ContentPanel: Save completed successfully')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[progress] ❌ ContentPanel: Failed to save reading position:', error)
|
console.error('[progress] ❌ ContentPanel: Failed to save reading position:', error)
|
||||||
}
|
}
|
||||||
@@ -205,7 +189,6 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
onReadingComplete: () => {
|
onReadingComplete: () => {
|
||||||
// Auto-mark as read when reading is complete (if enabled in settings)
|
// Auto-mark as read when reading is complete (if enabled in settings)
|
||||||
if (activeAccount && !isMarkedAsRead && settings?.autoMarkAsReadOnCompletion) {
|
if (activeAccount && !isMarkedAsRead && settings?.autoMarkAsReadOnCompletion) {
|
||||||
console.log('[progress] 📖 Auto-marking as read on completion')
|
|
||||||
handleMarkAsRead()
|
handleMarkAsRead()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,36 +196,17 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
|
|
||||||
// Log sync status when it changes
|
// Log sync status when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[progress] 📊 ContentPanel reading position sync status:', {
|
|
||||||
enabled: isTextContent,
|
|
||||||
syncEnabled: settings?.syncReadingPosition !== false,
|
|
||||||
hasAccount: !!activeAccount,
|
|
||||||
hasRelayPool: !!relayPool,
|
|
||||||
hasEventStore: !!eventStore,
|
|
||||||
hasArticleIdentifier: !!articleIdentifier,
|
|
||||||
currentProgress: progressPercentage + '%'
|
|
||||||
})
|
|
||||||
}, [isTextContent, settings?.syncReadingPosition, activeAccount, relayPool, eventStore, articleIdentifier, progressPercentage])
|
}, [isTextContent, settings?.syncReadingPosition, activeAccount, relayPool, eventStore, articleIdentifier, progressPercentage])
|
||||||
|
|
||||||
// Load saved reading position when article loads
|
// Load saved reading position when article loads
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isTextContent || !activeAccount || !relayPool || !eventStore || !articleIdentifier) {
|
if (!isTextContent || !activeAccount || !relayPool || !eventStore || !articleIdentifier) {
|
||||||
console.log('⏭️ [ContentPanel] Skipping position restore - missing requirements:', {
|
|
||||||
isTextContent,
|
|
||||||
hasAccount: !!activeAccount,
|
|
||||||
hasRelayPool: !!relayPool,
|
|
||||||
hasEventStore: !!eventStore,
|
|
||||||
hasIdentifier: !!articleIdentifier
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (settings?.syncReadingPosition === false) {
|
if (settings?.syncReadingPosition === false) {
|
||||||
console.log('⏭️ [ContentPanel] Sync disabled in settings - not restoring position')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📖 [ContentPanel] Loading position for article:', selectedUrl?.slice(0, 50))
|
|
||||||
|
|
||||||
const loadPosition = async () => {
|
const loadPosition = async () => {
|
||||||
try {
|
try {
|
||||||
const savedPosition = await loadReadingPosition(
|
const savedPosition = await loadReadingPosition(
|
||||||
@@ -253,7 +217,6 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (savedPosition && savedPosition.position > 0.05 && savedPosition.position < 1) {
|
if (savedPosition && savedPosition.position > 0.05 && savedPosition.position < 1) {
|
||||||
console.log('🎯 [ContentPanel] Restoring position:', Math.round(savedPosition.position * 100) + '%')
|
|
||||||
// Wait for content to be fully rendered before scrolling
|
// Wait for content to be fully rendered before scrolling
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const documentHeight = document.documentElement.scrollHeight
|
const documentHeight = document.documentElement.scrollHeight
|
||||||
@@ -264,14 +227,10 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
top: scrollTop,
|
top: scrollTop,
|
||||||
behavior: 'smooth'
|
behavior: 'smooth'
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('✅ [ContentPanel] Restored to position:', Math.round(savedPosition.position * 100) + '%', 'scrollTop:', scrollTop)
|
|
||||||
}, 500) // Give content time to render
|
}, 500) // Give content time to render
|
||||||
} else if (savedPosition) {
|
} else if (savedPosition) {
|
||||||
if (savedPosition.position === 1) {
|
if (savedPosition.position === 1) {
|
||||||
console.log('✅ [ContentPanel] Article completed (100%), starting from top')
|
|
||||||
} else {
|
} else {
|
||||||
console.log('⏭️ [ContentPanel] Position too early (<5%):', Math.round(savedPosition.position * 100) + '%')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -648,14 +607,12 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
activeAccount,
|
activeAccount,
|
||||||
relayPool
|
relayPool
|
||||||
)
|
)
|
||||||
console.log('✅ Marked nostr article as read')
|
|
||||||
} else if (selectedUrl) {
|
} else if (selectedUrl) {
|
||||||
await createWebsiteReaction(
|
await createWebsiteReaction(
|
||||||
selectedUrl,
|
selectedUrl,
|
||||||
activeAccount,
|
activeAccount,
|
||||||
relayPool
|
relayPool
|
||||||
)
|
)
|
||||||
console.log('✅ Marked website as read')
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to mark as read:', error)
|
console.error('Failed to mark as read:', error)
|
||||||
|
|||||||
@@ -310,7 +310,6 @@ const Debug: React.FC<DebugProps> = ({
|
|||||||
|
|
||||||
// Subscribe to decrypt complete events for Debug UI display
|
// Subscribe to decrypt complete events for Debug UI display
|
||||||
const unsubscribeDecrypt = bookmarkController.onDecryptComplete((eventId, publicCount, privateCount) => {
|
const unsubscribeDecrypt = bookmarkController.onDecryptComplete((eventId, publicCount, privateCount) => {
|
||||||
console.log('[bunker] ✅ Auto-decrypted:', eventId.slice(0, 8), {
|
|
||||||
public: publicCount,
|
public: publicCount,
|
||||||
private: privateCount
|
private: privateCount
|
||||||
})
|
})
|
||||||
@@ -742,7 +741,6 @@ const Debug: React.FC<DebugProps> = ({
|
|||||||
|
|
||||||
// Subscribe to controller updates to see streaming
|
// Subscribe to controller updates to see streaming
|
||||||
const unsubscribe = contactsController.onContacts((contacts) => {
|
const unsubscribe = contactsController.onContacts((contacts) => {
|
||||||
console.log('[debug] Received contacts update:', contacts.size)
|
|
||||||
setFriendsPubkeys(new Set(contacts))
|
setFriendsPubkeys(new Set(contacts))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -178,12 +178,10 @@ const Explore: React.FC<ExploreProps> = ({ relayPool, eventStore, settings, acti
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Get initial state immediately
|
// Get initial state immediately
|
||||||
const initialMap = readingProgressController.getProgressMap()
|
const initialMap = readingProgressController.getProgressMap()
|
||||||
console.log('[progress] 🎯 Explore: Initial progress map size:', initialMap.size)
|
|
||||||
setReadingProgressMap(initialMap)
|
setReadingProgressMap(initialMap)
|
||||||
|
|
||||||
// Subscribe to updates
|
// Subscribe to updates
|
||||||
const unsubProgress = readingProgressController.onProgress((newMap) => {
|
const unsubProgress = readingProgressController.onProgress((newMap) => {
|
||||||
console.log('[progress] 🎯 Explore: Received progress update, size:', newMap.size)
|
|
||||||
setReadingProgressMap(newMap)
|
setReadingProgressMap(newMap)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -612,7 +610,6 @@ const Explore: React.FC<ExploreProps> = ({ relayPool, eventStore, settings, acti
|
|||||||
const getReadingProgress = useCallback((post: BlogPostPreview): number | undefined => {
|
const getReadingProgress = useCallback((post: BlogPostPreview): number | undefined => {
|
||||||
const dTag = post.event.tags.find(t => t[0] === 'd')?.[1]
|
const dTag = post.event.tags.find(t => t[0] === 'd')?.[1]
|
||||||
if (!dTag) {
|
if (!dTag) {
|
||||||
console.log('[progress] ⚠️ No d-tag for post:', post.title)
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,16 +621,6 @@ const Explore: React.FC<ExploreProps> = ({ relayPool, eventStore, settings, acti
|
|||||||
})
|
})
|
||||||
const progress = readingProgressMap.get(naddr)
|
const progress = readingProgressMap.get(naddr)
|
||||||
|
|
||||||
// Only log first lookup to avoid spam, or when found
|
|
||||||
if (progress || readingProgressMap.size === 0) {
|
|
||||||
console.log('[progress] 🔍 Looking up:', {
|
|
||||||
title: post.title.slice(0, 30),
|
|
||||||
naddr: naddr.slice(0, 80),
|
|
||||||
mapSize: readingProgressMap.size,
|
|
||||||
mapKeys: readingProgressMap.size > 0 ? Array.from(readingProgressMap.keys()).slice(0, 3).map(k => k.slice(0, 80)) : [],
|
|
||||||
progress: progress ? Math.round(progress * 100) + '%' : 'not found'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return progress
|
return progress
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[progress] ❌ Error encoding naddr:', err)
|
console.error('[progress] ❌ Error encoding naddr:', err)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export const HighlightCitation: React.FC<HighlightCitationProps> = ({
|
|||||||
// Fallback: extract directly from p tag
|
// Fallback: extract directly from p tag
|
||||||
const pTag = highlight.tags.find(t => t[0] === 'p')
|
const pTag = highlight.tags.find(t => t[0] === 'p')
|
||||||
if (pTag && pTag[1]) {
|
if (pTag && pTag[1]) {
|
||||||
console.log('📝 Found author from p tag:', pTag[1])
|
|
||||||
return pTag[1]
|
return pTag[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -348,11 +348,9 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({
|
|||||||
// Publish to all configured relays - let the relay pool handle connection state
|
// Publish to all configured relays - let the relay pool handle connection state
|
||||||
const targetRelays = RELAYS
|
const targetRelays = RELAYS
|
||||||
|
|
||||||
console.log('📡 Rebroadcasting highlight to', targetRelays.length, 'relay(s):', targetRelays)
|
|
||||||
|
|
||||||
await relayPool.publish(targetRelays, event)
|
await relayPool.publish(targetRelays, event)
|
||||||
|
|
||||||
console.log('✅ Rebroadcast successful!')
|
|
||||||
|
|
||||||
// Update the highlight with new relay info
|
// Update the highlight with new relay info
|
||||||
const isLocalOnly = areAllRelaysLocal(targetRelays)
|
const isLocalOnly = areAllRelaysLocal(targetRelays)
|
||||||
@@ -449,7 +447,6 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({
|
|||||||
relayPool
|
relayPool
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('✅ Highlight deletion request published')
|
|
||||||
|
|
||||||
// Notify parent to remove this highlight from the list
|
// Notify parent to remove this highlight from the list
|
||||||
if (onHighlightDelete) {
|
if (onHighlightDelete) {
|
||||||
|
|||||||
@@ -243,23 +243,15 @@ const Me: React.FC<MeProps> = ({
|
|||||||
// Background enrichment: merge reading progress and mark-as-read
|
// Background enrichment: merge reading progress and mark-as-read
|
||||||
// Only update items that are already in our map
|
// Only update items that are already in our map
|
||||||
fetchAllReads(relayPool, viewingPubkey, bookmarks, (item) => {
|
fetchAllReads(relayPool, viewingPubkey, bookmarks, (item) => {
|
||||||
console.log('📈 [Reads] Enrichment item received:', {
|
|
||||||
id: item.id.slice(0, 20) + '...',
|
|
||||||
progress: item.readingProgress,
|
|
||||||
hasProgress: item.readingProgress !== undefined && item.readingProgress > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
setReadsMap(prevMap => {
|
setReadsMap(prevMap => {
|
||||||
// Only update if item exists in our current map
|
// Only update if item exists in our current map
|
||||||
if (!prevMap.has(item.id)) {
|
if (!prevMap.has(item.id)) {
|
||||||
console.log('⚠️ [Reads] Item not in map, skipping:', item.id.slice(0, 20) + '...')
|
|
||||||
return prevMap
|
return prevMap
|
||||||
}
|
}
|
||||||
|
|
||||||
const newMap = new Map(prevMap)
|
const newMap = new Map(prevMap)
|
||||||
const merged = mergeReadItem(newMap, item)
|
const merged = mergeReadItem(newMap, item)
|
||||||
if (merged) {
|
if (merged) {
|
||||||
console.log('✅ [Reads] Merged progress:', item.id.slice(0, 20) + '...', item.readingProgress)
|
|
||||||
// Update reads array after map is updated
|
// Update reads array after map is updated
|
||||||
setReads(Array.from(newMap.values()))
|
setReads(Array.from(newMap.values()))
|
||||||
return newMap
|
return newMap
|
||||||
|
|||||||
@@ -68,12 +68,10 @@ const Profile: React.FC<ProfileProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Get initial state immediately
|
// Get initial state immediately
|
||||||
const initialMap = readingProgressController.getProgressMap()
|
const initialMap = readingProgressController.getProgressMap()
|
||||||
console.log('[progress] 🎯 Profile: Initial progress map size:', initialMap.size)
|
|
||||||
setReadingProgressMap(initialMap)
|
setReadingProgressMap(initialMap)
|
||||||
|
|
||||||
// Subscribe to updates
|
// Subscribe to updates
|
||||||
const unsubProgress = readingProgressController.onProgress((newMap) => {
|
const unsubProgress = readingProgressController.onProgress((newMap) => {
|
||||||
console.log('[progress] 🎯 Profile: Received progress update, size:', newMap.size)
|
|
||||||
setReadingProgressMap(newMap)
|
setReadingProgressMap(newMap)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -100,12 +98,10 @@ const Profile: React.FC<ProfileProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!pubkey || !relayPool || !eventStore) return
|
if (!pubkey || !relayPool || !eventStore) return
|
||||||
|
|
||||||
console.log('🔄 [Profile] Background fetching highlights and writings for', pubkey.slice(0, 8))
|
|
||||||
|
|
||||||
// Fetch highlights in background
|
// Fetch highlights in background
|
||||||
fetchHighlights(relayPool, pubkey, undefined, undefined, false, eventStore)
|
fetchHighlights(relayPool, pubkey, undefined, undefined, false, eventStore)
|
||||||
.then(highlights => {
|
.then(highlights => {
|
||||||
console.log('✅ [Profile] Fetched', highlights.length, 'highlights')
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.warn('⚠️ [Profile] Failed to fetch highlights:', err)
|
console.warn('⚠️ [Profile] Failed to fetch highlights:', err)
|
||||||
@@ -115,7 +111,6 @@ const Profile: React.FC<ProfileProps> = ({
|
|||||||
fetchBlogPostsFromAuthors(relayPool, [pubkey], RELAYS, undefined, null)
|
fetchBlogPostsFromAuthors(relayPool, [pubkey], RELAYS, undefined, null)
|
||||||
.then(writings => {
|
.then(writings => {
|
||||||
writings.forEach(w => eventStore.add(w.event))
|
writings.forEach(w => eventStore.add(w.event))
|
||||||
console.log('✅ [Profile] Fetched', writings.length, 'writings')
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.warn('⚠️ [Profile] Failed to fetch writings:', err)
|
console.warn('⚠️ [Profile] Failed to fetch writings:', err)
|
||||||
@@ -157,7 +152,6 @@ const Profile: React.FC<ProfileProps> = ({
|
|||||||
|
|
||||||
// Only log when found or map is empty
|
// Only log when found or map is empty
|
||||||
if (progress || readingProgressMap.size === 0) {
|
if (progress || readingProgressMap.size === 0) {
|
||||||
console.log('[progress] 🔍 Profile lookup:', {
|
|
||||||
title: post.title?.slice(0, 30),
|
title: post.title?.slice(0, 30),
|
||||||
naddr: naddr.slice(0, 80),
|
naddr: naddr.slice(0, 80),
|
||||||
mapSize: readingProgressMap.size,
|
mapSize: readingProgressMap.size,
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export const RelayStatusIndicator: React.FC<RelayStatusIndicatorProps> = ({
|
|||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🔌 Relay Status Indicator:', {
|
|
||||||
mode: isConnecting ? 'CONNECTING' : offlineMode ? 'OFFLINE' : localOnlyMode ? 'LOCAL_ONLY' : 'ONLINE',
|
mode: isConnecting ? 'CONNECTING' : offlineMode ? 'OFFLINE' : localOnlyMode ? 'LOCAL_ONLY' : 'ONLINE',
|
||||||
totalStatuses: relayStatuses.length,
|
totalStatuses: relayStatuses.length,
|
||||||
connectedCount: connectedUrls.length,
|
connectedCount: connectedUrls.length,
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const PWASettings: React.FC<PWASettingsProps> = ({ settings, onUpdate, onClose }
|
|||||||
if (isInstalled) return
|
if (isInstalled) return
|
||||||
const success = await installApp()
|
const success = await installApp()
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log('App installed successfully')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export function useAdaptiveTextColor(imageUrl: string | undefined): AdaptiveText
|
|||||||
height: Math.floor(height * 0.25)
|
height: Math.floor(height * 0.25)
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('Adaptive color detected:', {
|
|
||||||
hex: color.hex,
|
hex: color.hex,
|
||||||
rgb: color.rgb,
|
rgb: color.rgb,
|
||||||
isLight: color.isLight,
|
isLight: color.isLight,
|
||||||
@@ -52,12 +51,10 @@ export function useAdaptiveTextColor(imageUrl: string | undefined): AdaptiveText
|
|||||||
|
|
||||||
// Use library's built-in isLight check for optimal contrast
|
// Use library's built-in isLight check for optimal contrast
|
||||||
if (color.isLight) {
|
if (color.isLight) {
|
||||||
console.log('Light background detected, using black text')
|
|
||||||
setColors({
|
setColors({
|
||||||
textColor: '#000000'
|
textColor: '#000000'
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('Dark background detected, using white text')
|
|
||||||
setColors({
|
setColors({
|
||||||
textColor: '#ffffff'
|
textColor: '#ffffff'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -64,8 +64,6 @@ export function useArticleLoader({
|
|||||||
setCurrentArticleEventId(article.event.id)
|
setCurrentArticleEventId(article.event.id)
|
||||||
setCurrentArticle?.(article.event)
|
setCurrentArticle?.(article.event)
|
||||||
|
|
||||||
console.log('📰 Article loaded:', article.title)
|
|
||||||
console.log('📍 Coordinate:', articleCoordinate)
|
|
||||||
|
|
||||||
// Set reader loading to false immediately after article content is ready
|
// Set reader loading to false immediately after article content is ready
|
||||||
// Don't wait for highlights to finish loading
|
// Don't wait for highlights to finish loading
|
||||||
@@ -92,7 +90,6 @@ export function useArticleLoader({
|
|||||||
},
|
},
|
||||||
settings
|
settings
|
||||||
)
|
)
|
||||||
console.log(`📌 Found ${highlightsMap.size} highlights`)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch highlights:', err)
|
console.error('Failed to fetch highlights:', err)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ export function useExternalUrlLoader({
|
|||||||
const content = await fetchReadableContent(url)
|
const content = await fetchReadableContent(url)
|
||||||
setReaderContent(content)
|
setReaderContent(content)
|
||||||
|
|
||||||
console.log('🌐 External URL loaded:', content.title)
|
|
||||||
|
|
||||||
// Set reader loading to false immediately after content is ready
|
// Set reader loading to false immediately after content is ready
|
||||||
setReaderLoading(false)
|
setReaderLoading(false)
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ export const useHighlightCreation = ({
|
|||||||
? currentArticle.content
|
? currentArticle.content
|
||||||
: readerContent?.markdown || readerContent?.html
|
: readerContent?.markdown || readerContent?.html
|
||||||
|
|
||||||
console.log('🎯 Creating highlight...', { text: text.substring(0, 50) + '...' })
|
|
||||||
|
|
||||||
const newHighlight = await createHighlight(
|
const newHighlight = await createHighlight(
|
||||||
text,
|
text,
|
||||||
@@ -73,7 +72,6 @@ export const useHighlightCreation = ({
|
|||||||
settings
|
settings
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('✅ Highlight created successfully!', {
|
|
||||||
id: newHighlight.id,
|
id: newHighlight.id,
|
||||||
isLocalOnly: newHighlight.isLocalOnly,
|
isLocalOnly: newHighlight.isLocalOnly,
|
||||||
isOfflineCreated: newHighlight.isOfflineCreated,
|
isOfflineCreated: newHighlight.isOfflineCreated,
|
||||||
|
|||||||
@@ -32,14 +32,12 @@ export const useHighlightedContent = ({
|
|||||||
}: UseHighlightedContentParams) => {
|
}: UseHighlightedContentParams) => {
|
||||||
// Filter highlights by URL and visibility settings
|
// Filter highlights by URL and visibility settings
|
||||||
const relevantHighlights = useMemo(() => {
|
const relevantHighlights = useMemo(() => {
|
||||||
console.log('🔍 ContentPanel: Processing highlights', {
|
|
||||||
totalHighlights: highlights.length,
|
totalHighlights: highlights.length,
|
||||||
selectedUrl,
|
selectedUrl,
|
||||||
showHighlights
|
showHighlights
|
||||||
})
|
})
|
||||||
|
|
||||||
const urlFiltered = filterHighlightsByUrl(highlights, selectedUrl)
|
const urlFiltered = filterHighlightsByUrl(highlights, selectedUrl)
|
||||||
console.log('📌 URL filtered highlights:', urlFiltered.length)
|
|
||||||
|
|
||||||
// Apply visibility filtering
|
// Apply visibility filtering
|
||||||
const classified = classifyHighlights(urlFiltered, currentUserPubkey, followedPubkeys)
|
const classified = classifyHighlights(urlFiltered, currentUserPubkey, followedPubkeys)
|
||||||
@@ -49,7 +47,6 @@ export const useHighlightedContent = ({
|
|||||||
return highlightVisibility.nostrverse
|
return highlightVisibility.nostrverse
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('✅ Relevant highlights after filtering:', filtered.length, filtered.map(h => h.content.substring(0, 30)))
|
|
||||||
return filtered
|
return filtered
|
||||||
}, [selectedUrl, highlights, highlightVisibility, currentUserPubkey, followedPubkeys, showHighlights])
|
}, [selectedUrl, highlights, highlightVisibility, currentUserPubkey, followedPubkeys, showHighlights])
|
||||||
|
|
||||||
@@ -57,7 +54,6 @@ export const useHighlightedContent = ({
|
|||||||
const finalHtml = useMemo(() => {
|
const finalHtml = useMemo(() => {
|
||||||
const sourceHtml = markdown ? renderedMarkdownHtml : html
|
const sourceHtml = markdown ? renderedMarkdownHtml : html
|
||||||
|
|
||||||
console.log('🎨 Preparing final HTML:', {
|
|
||||||
hasMarkdown: !!markdown,
|
hasMarkdown: !!markdown,
|
||||||
hasHtml: !!html,
|
hasHtml: !!html,
|
||||||
renderedHtmlLength: renderedMarkdownHtml.length,
|
renderedHtmlLength: renderedMarkdownHtml.length,
|
||||||
@@ -72,13 +68,10 @@ export const useHighlightedContent = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showHighlights && relevantHighlights.length > 0) {
|
if (showHighlights && relevantHighlights.length > 0) {
|
||||||
console.log('✨ Applying', relevantHighlights.length, 'highlights to HTML')
|
|
||||||
const highlightedHtml = applyHighlightsToHTML(sourceHtml, relevantHighlights, highlightStyle)
|
const highlightedHtml = applyHighlightsToHTML(sourceHtml, relevantHighlights, highlightStyle)
|
||||||
console.log('✅ Highlights applied, result length:', highlightedHtml.length)
|
|
||||||
return highlightedHtml
|
return highlightedHtml
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📄 Returning source HTML without highlights')
|
|
||||||
return sourceHtml
|
return sourceHtml
|
||||||
}, [html, renderedMarkdownHtml, markdown, relevantHighlights, showHighlights, highlightStyle])
|
}, [html, renderedMarkdownHtml, markdown, relevantHighlights, showHighlights, highlightStyle])
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export const useMarkdownToHTML = (
|
|||||||
|
|
||||||
// Replace nostr URIs with resolved titles
|
// Replace nostr URIs with resolved titles
|
||||||
processed = replaceNostrUrisInMarkdownWithTitles(markdown, articleTitles)
|
processed = replaceNostrUrisInMarkdownWithTitles(markdown, articleTitles)
|
||||||
console.log(`📚 Resolved ${articleTitles.size} article titles`)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to fetch article titles:', error)
|
console.warn('Failed to fetch article titles:', error)
|
||||||
// Fall back to basic replacement
|
// Fall back to basic replacement
|
||||||
@@ -58,12 +57,10 @@ export const useMarkdownToHTML = (
|
|||||||
|
|
||||||
setProcessedMarkdown(processed)
|
setProcessedMarkdown(processed)
|
||||||
|
|
||||||
console.log('📝 Converting markdown to HTML...')
|
|
||||||
|
|
||||||
const rafId = requestAnimationFrame(() => {
|
const rafId = requestAnimationFrame(() => {
|
||||||
if (previewRef.current && !isCancelled) {
|
if (previewRef.current && !isCancelled) {
|
||||||
const html = previewRef.current.innerHTML
|
const html = previewRef.current.innerHTML
|
||||||
console.log('✅ Markdown converted to HTML:', html.length, 'chars')
|
|
||||||
setRenderedHtml(html)
|
setRenderedHtml(html)
|
||||||
} else if (!isCancelled) {
|
} else if (!isCancelled) {
|
||||||
console.warn('⚠️ markdownPreviewRef.current is null')
|
console.warn('⚠️ markdownPreviewRef.current is null')
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ export function useOfflineSync({
|
|||||||
const isNowOnline = hasRemoteRelays
|
const isNowOnline = hasRemoteRelays
|
||||||
|
|
||||||
if (wasLocalOnly && isNowOnline) {
|
if (wasLocalOnly && isNowOnline) {
|
||||||
console.log('✈️ Detected transition: Flight Mode → Online')
|
|
||||||
console.log('📊 Relay state:', {
|
|
||||||
connectedRelays: connectedRelays.length,
|
connectedRelays: connectedRelays.length,
|
||||||
remoteRelays: connectedRelays.filter(r => !isLocalRelay(r.url)).length,
|
remoteRelays: connectedRelays.filter(r => !isLocalRelay(r.url)).length,
|
||||||
localRelays: connectedRelays.filter(r => isLocalRelay(r.url)).length
|
localRelays: connectedRelays.filter(r => isLocalRelay(r.url)).length
|
||||||
@@ -59,7 +57,6 @@ export function useOfflineSync({
|
|||||||
|
|
||||||
// Wait a moment for relays to fully establish connections
|
// Wait a moment for relays to fully establish connections
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('🚀 Starting sync after delay...')
|
|
||||||
syncLocalEventsToRemote(relayPool, eventStore)
|
syncLocalEventsToRemote(relayPool, eventStore)
|
||||||
}, 2000)
|
}, 2000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ export function useOnlineStatus() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleOnline = () => {
|
const handleOnline = () => {
|
||||||
console.log('🌐 Back online')
|
|
||||||
setIsOnline(true)
|
setIsOnline(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOffline = () => {
|
const handleOffline = () => {
|
||||||
console.log('📴 Gone offline')
|
|
||||||
setIsOnline(false)
|
setIsOnline(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,12 +51,10 @@ export function usePWAInstall() {
|
|||||||
const choiceResult = await deferredPrompt.userChoice
|
const choiceResult = await deferredPrompt.userChoice
|
||||||
|
|
||||||
if (choiceResult.outcome === 'accepted') {
|
if (choiceResult.outcome === 'accepted') {
|
||||||
console.log('✅ PWA installed')
|
|
||||||
setIsInstallable(false)
|
setIsInstallable(false)
|
||||||
setDeferredPrompt(null)
|
setDeferredPrompt(null)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ PWA installation dismissed')
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export const useReadingPosition = ({
|
|||||||
// Debounced save function
|
// Debounced save function
|
||||||
const scheduleSave = useCallback((currentPosition: number) => {
|
const scheduleSave = useCallback((currentPosition: number) => {
|
||||||
if (!syncEnabled || !onSave) {
|
if (!syncEnabled || !onSave) {
|
||||||
console.log('[progress] ⏭️ scheduleSave skipped:', { syncEnabled, hasOnSave: !!onSave, position: Math.round(currentPosition * 100) + '%' })
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +42,6 @@ export const useReadingPosition = ({
|
|||||||
const isInitialSave = !hasSavedOnce.current
|
const isInitialSave = !hasSavedOnce.current
|
||||||
|
|
||||||
if (!hasSignificantChange && !hasReachedCompletion && !isInitialSave) {
|
if (!hasSignificantChange && !hasReachedCompletion && !isInitialSave) {
|
||||||
console.log('[progress] ⏭️ No significant change:', {
|
|
||||||
current: Math.round(currentPosition * 100) + '%',
|
current: Math.round(currentPosition * 100) + '%',
|
||||||
last: Math.round(lastSavedPosition.current * 100) + '%',
|
last: Math.round(lastSavedPosition.current * 100) + '%',
|
||||||
diff: Math.abs(currentPosition - lastSavedPosition.current),
|
diff: Math.abs(currentPosition - lastSavedPosition.current),
|
||||||
@@ -58,9 +56,7 @@ export const useReadingPosition = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Schedule new save
|
// Schedule new save
|
||||||
console.log('[progress] ⏰ Scheduling save in', autoSaveInterval + 'ms for position:', Math.round(currentPosition * 100) + '%')
|
|
||||||
saveTimerRef.current = setTimeout(() => {
|
saveTimerRef.current = setTimeout(() => {
|
||||||
console.log('[progress] 💾 Auto-saving position:', Math.round(currentPosition * 100) + '%')
|
|
||||||
lastSavedPosition.current = currentPosition
|
lastSavedPosition.current = currentPosition
|
||||||
hasSavedOnce.current = true
|
hasSavedOnce.current = true
|
||||||
onSave(currentPosition)
|
onSave(currentPosition)
|
||||||
@@ -78,7 +74,6 @@ export const useReadingPosition = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Always allow immediate save (including 0%)
|
// Always allow immediate save (including 0%)
|
||||||
console.log('[progress] 💾 Immediate save triggered for position:', Math.round(position * 100) + '%')
|
|
||||||
lastSavedPosition.current = position
|
lastSavedPosition.current = position
|
||||||
hasSavedOnce.current = true
|
hasSavedOnce.current = true
|
||||||
onSave(position)
|
onSave(position)
|
||||||
@@ -109,7 +104,6 @@ export const useReadingPosition = ({
|
|||||||
const prevPercent = Math.floor(position * 20) // Groups by 5%
|
const prevPercent = Math.floor(position * 20) // Groups by 5%
|
||||||
const newPercent = Math.floor(clampedProgress * 20)
|
const newPercent = Math.floor(clampedProgress * 20)
|
||||||
if (prevPercent !== newPercent) {
|
if (prevPercent !== newPercent) {
|
||||||
console.log('[progress] 📏 useReadingPosition:', Math.round(clampedProgress * 100) + '%', {
|
|
||||||
scrollTop,
|
scrollTop,
|
||||||
documentHeight,
|
documentHeight,
|
||||||
isAtBottom
|
isAtBottom
|
||||||
@@ -131,12 +125,10 @@ export const useReadingPosition = ({
|
|||||||
if (!hasTriggeredComplete.current && position === 1) {
|
if (!hasTriggeredComplete.current && position === 1) {
|
||||||
setIsReadingComplete(true)
|
setIsReadingComplete(true)
|
||||||
hasTriggeredComplete.current = true
|
hasTriggeredComplete.current = true
|
||||||
console.log('[progress] ✅ Completion hold satisfied (100% for', completionHoldMs, 'ms)')
|
|
||||||
onReadingComplete?.()
|
onReadingComplete?.()
|
||||||
}
|
}
|
||||||
completionTimerRef.current = null
|
completionTimerRef.current = null
|
||||||
}, completionHoldMs)
|
}, completionHoldMs)
|
||||||
console.log('[progress] ⏳ Completion hold started (waiting', completionHoldMs, 'ms)')
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we moved off 100%, cancel any pending completion hold
|
// If we moved off 100%, cancel any pending completion hold
|
||||||
@@ -147,7 +139,6 @@ export const useReadingPosition = ({
|
|||||||
if (clampedProgress >= readingCompleteThreshold) {
|
if (clampedProgress >= readingCompleteThreshold) {
|
||||||
setIsReadingComplete(true)
|
setIsReadingComplete(true)
|
||||||
hasTriggeredComplete.current = true
|
hasTriggeredComplete.current = true
|
||||||
console.log('[progress] ✅ Completion via threshold:', readingCompleteThreshold)
|
|
||||||
onReadingComplete?.()
|
onReadingComplete?.()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
|
|||||||
const root = document.documentElement.style
|
const root = document.documentElement.style
|
||||||
const fontKey = settings.readingFont || 'system'
|
const fontKey = settings.readingFont || 'system'
|
||||||
|
|
||||||
console.log('🎨 Applying settings styles:', { fontKey, fontSize: settings.fontSize, theme: settings.theme })
|
|
||||||
|
|
||||||
// Apply theme with color variants (defaults to 'system' if not set)
|
// Apply theme with color variants (defaults to 'system' if not set)
|
||||||
applyTheme(
|
applyTheme(
|
||||||
@@ -59,9 +58,7 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
|
|||||||
|
|
||||||
// Load font first and wait for it to be ready
|
// Load font first and wait for it to be ready
|
||||||
if (fontKey !== 'system') {
|
if (fontKey !== 'system') {
|
||||||
console.log('⏳ Waiting for font to load...')
|
|
||||||
await loadFont(fontKey)
|
await loadFont(fontKey)
|
||||||
console.log('✅ Font loaded, applying styles')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply font settings after font is loaded
|
// Apply font settings after font is loaded
|
||||||
@@ -76,7 +73,6 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
|
|||||||
// Set paragraph alignment
|
// Set paragraph alignment
|
||||||
root.setProperty('--paragraph-alignment', settings.paragraphAlignment || 'justify')
|
root.setProperty('--paragraph-alignment', settings.paragraphAlignment || 'justify')
|
||||||
|
|
||||||
console.log('✅ All styles applied')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applyStyles()
|
applyStyles()
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ if ('serviceWorker' in navigator) {
|
|||||||
navigator.serviceWorker
|
navigator.serviceWorker
|
||||||
.register('/sw.js', { type: 'module' })
|
.register('/sw.js', { type: 'module' })
|
||||||
.then(registration => {
|
.then(registration => {
|
||||||
console.log('✅ Service Worker registered:', registration.scope)
|
|
||||||
|
|
||||||
// Check for updates periodically
|
// Check for updates periodically
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@@ -25,7 +24,6 @@ if ('serviceWorker' in navigator) {
|
|||||||
newWorker.addEventListener('statechange', () => {
|
newWorker.addEventListener('statechange', () => {
|
||||||
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
||||||
// New service worker available
|
// New service worker available
|
||||||
console.log('🔄 New version available! Reload to update.')
|
|
||||||
|
|
||||||
// Optionally show a toast notification
|
// Optionally show a toast notification
|
||||||
const updateAvailable = new CustomEvent('sw-update-available')
|
const updateAvailable = new CustomEvent('sw-update-available')
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ function getFromCache(naddr: string): ArticleContent | null {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📦 Loaded article from cache:', naddr)
|
|
||||||
return content
|
return content
|
||||||
} catch {
|
} catch {
|
||||||
return null
|
return null
|
||||||
@@ -63,7 +62,6 @@ function saveToCache(naddr: string, content: ArticleContent): void {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
}
|
}
|
||||||
localStorage.setItem(cacheKey, JSON.stringify(cached))
|
localStorage.setItem(cacheKey, JSON.stringify(cached))
|
||||||
console.log('💾 Saved article to cache:', naddr)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Failed to cache article:', err)
|
console.warn('Failed to cache article:', err)
|
||||||
// Silently fail if storage is full or unavailable
|
// Silently fail if storage is full or unavailable
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ async function decryptEvent(
|
|||||||
try {
|
try {
|
||||||
await Helpers.unlockHiddenTags(evt, signerCandidate as HiddenContentSigner, 'nip44' as UnlockMode)
|
await Helpers.unlockHiddenTags(evt, signerCandidate as HiddenContentSigner, 'nip44' as UnlockMode)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("[bunker] ❌ nip44.decrypt failed:", err instanceof Error ? err.message : String(err))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (evt.content && evt.content.length > 0) {
|
} else if (evt.content && evt.content.length > 0) {
|
||||||
@@ -46,7 +45,6 @@ async function decryptEvent(
|
|||||||
try {
|
try {
|
||||||
decryptedContent = await (signerCandidate as { nip44: { decrypt: DecryptFn } }).nip44.decrypt(evt.pubkey, evt.content)
|
decryptedContent = await (signerCandidate as { nip44: { decrypt: DecryptFn } }).nip44.decrypt(evt.pubkey, evt.content)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("[bunker] ❌ nip44.decrypt failed:", err instanceof Error ? err.message : String(err))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +53,6 @@ async function decryptEvent(
|
|||||||
try {
|
try {
|
||||||
decryptedContent = await (signerCandidate as { nip04: { decrypt: DecryptFn } }).nip04.decrypt(evt.pubkey, evt.content)
|
decryptedContent = await (signerCandidate as { nip04: { decrypt: DecryptFn } }).nip04.decrypt(evt.pubkey, evt.content)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("[bunker] ❌ nip04.decrypt failed:", err instanceof Error ? err.message : String(err))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export const fetchContacts = async (
|
|||||||
): Promise<Set<string>> => {
|
): Promise<Set<string>> => {
|
||||||
try {
|
try {
|
||||||
const relayUrls = prioritizeLocalRelays(Array.from(relayPool.relays.values()).map(relay => relay.url))
|
const relayUrls = prioritizeLocalRelays(Array.from(relayPool.relays.values()).map(relay => relay.url))
|
||||||
console.log('🔍 Fetching contacts (kind 3) for user:', pubkey)
|
|
||||||
|
|
||||||
const partialFollowed = new Set<string>()
|
const partialFollowed = new Set<string>()
|
||||||
const events = await queryEvents(
|
const events = await queryEvents(
|
||||||
@@ -51,9 +50,7 @@ export const fetchContacts = async (
|
|||||||
}
|
}
|
||||||
// merged already via streams
|
// merged already via streams
|
||||||
|
|
||||||
console.log('📊 Contact events fetched:', events.length)
|
|
||||||
|
|
||||||
console.log('👥 Followed contacts:', followed.size)
|
|
||||||
return followed
|
return followed
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch contacts:', error)
|
console.error('Failed to fetch contacts:', error)
|
||||||
|
|||||||
@@ -73,13 +73,11 @@ class ContactsController {
|
|||||||
|
|
||||||
// Skip if already loaded for this pubkey (unless forced)
|
// Skip if already loaded for this pubkey (unless forced)
|
||||||
if (!force && this.isLoadedFor(pubkey)) {
|
if (!force && this.isLoadedFor(pubkey)) {
|
||||||
console.log('[contacts] ✅ Already loaded for', pubkey.slice(0, 8))
|
|
||||||
this.emitContacts(this.currentContacts)
|
this.emitContacts(this.currentContacts)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setLoading(true)
|
this.setLoading(true)
|
||||||
console.log('[contacts] 🔍 Loading contacts for', pubkey.slice(0, 8))
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const contacts = await fetchContacts(
|
const contacts = await fetchContacts(
|
||||||
@@ -89,7 +87,6 @@ class ContactsController {
|
|||||||
// Stream partial updates
|
// Stream partial updates
|
||||||
this.currentContacts = new Set(partial)
|
this.currentContacts = new Set(partial)
|
||||||
this.emitContacts(this.currentContacts)
|
this.emitContacts(this.currentContacts)
|
||||||
console.log('[contacts] 📥 Partial contacts:', partial.size)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -98,7 +95,6 @@ class ContactsController {
|
|||||||
this.lastLoadedPubkey = pubkey
|
this.lastLoadedPubkey = pubkey
|
||||||
this.emitContacts(this.currentContacts)
|
this.emitContacts(this.currentContacts)
|
||||||
|
|
||||||
console.log('[contacts] ✅ Loaded', contacts.size, 'contacts')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[contacts] ❌ Failed to load contacts:', error)
|
console.error('[contacts] ❌ Failed to load contacts:', error)
|
||||||
this.currentContacts.clear()
|
this.currentContacts.clear()
|
||||||
|
|||||||
@@ -36,12 +36,10 @@ export async function createDeletionRequest(
|
|||||||
|
|
||||||
const signed = await factory.sign(draft)
|
const signed = await factory.sign(draft)
|
||||||
|
|
||||||
console.log('🗑️ Created kind:5 deletion request for event:', eventId.slice(0, 8))
|
|
||||||
|
|
||||||
// Publish to relays
|
// Publish to relays
|
||||||
await relayPool.publish(RELAYS, signed)
|
await relayPool.publish(RELAYS, signed)
|
||||||
|
|
||||||
console.log('✅ Deletion request published to', RELAYS.length, 'relay(s)')
|
|
||||||
|
|
||||||
return signed
|
return signed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,9 @@ export const fetchBlogPostsFromAuthors = async (
|
|||||||
): Promise<BlogPostPreview[]> => {
|
): Promise<BlogPostPreview[]> => {
|
||||||
try {
|
try {
|
||||||
if (pubkeys.length === 0) {
|
if (pubkeys.length === 0) {
|
||||||
console.log('⚠️ No pubkeys to fetch blog posts from')
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📚 Fetching blog posts (kind 30023) from', pubkeys.length, 'authors', limit ? `(limit: ${limit})` : '(no limit)')
|
|
||||||
|
|
||||||
// Deduplicate replaceable events by keeping the most recent version
|
// Deduplicate replaceable events by keeping the most recent version
|
||||||
// Group by author + d-tag identifier
|
// Group by author + d-tag identifier
|
||||||
@@ -75,7 +73,6 @@ export const fetchBlogPostsFromAuthors = async (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('📊 Blog post events fetched (unique):', uniqueEvents.size)
|
|
||||||
|
|
||||||
// Convert to blog post previews and sort by published date (most recent first)
|
// Convert to blog post previews and sort by published date (most recent first)
|
||||||
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
|
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
|
||||||
@@ -97,7 +94,6 @@ export const fetchBlogPostsFromAuthors = async (
|
|||||||
return timeB - timeA // Most recent first
|
return timeB - timeA // Most recent first
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('📰 Processed', blogPosts.length, 'unique blog posts')
|
|
||||||
|
|
||||||
return blogPosts
|
return blogPosts
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export async function createHighlight(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create EventFactory with the account as signer
|
// Create EventFactory with the account as signer
|
||||||
console.log("[bunker] Creating EventFactory with signer:", { signerType: account.signer?.constructor?.name })
|
|
||||||
const factory = new EventFactory({ signer: account.signer })
|
const factory = new EventFactory({ signer: account.signer })
|
||||||
|
|
||||||
let blueprintSource: NostrEvent | AddressPointer | string
|
let blueprintSource: NostrEvent | AddressPointer | string
|
||||||
@@ -117,9 +116,7 @@ export async function createHighlight(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sign the event
|
// Sign the event
|
||||||
console.log('[bunker] Signing highlight event...', { kind: highlightEvent.kind, tags: highlightEvent.tags.length })
|
|
||||||
const signedEvent = await factory.sign(highlightEvent)
|
const signedEvent = await factory.sign(highlightEvent)
|
||||||
console.log('[bunker] ✅ Highlight signed successfully!', { id: signedEvent.id.slice(0, 8) })
|
|
||||||
|
|
||||||
// Use unified write service to store and publish
|
// Use unified write service to store and publish
|
||||||
await publishEvent(relayPool, eventStore, signedEvent)
|
await publishEvent(relayPool, eventStore, signedEvent)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const fetchHighlights = async (
|
|||||||
const cacheKey = highlightCache.authorKey(pubkey)
|
const cacheKey = highlightCache.authorKey(pubkey)
|
||||||
const cached = highlightCache.get(cacheKey)
|
const cached = highlightCache.get(cacheKey)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
console.log(`📌 Using cached highlights for author (${cached.length} items)`)
|
|
||||||
// Stream cached highlights if callback provided
|
// Stream cached highlights if callback provided
|
||||||
if (onHighlight) {
|
if (onHighlight) {
|
||||||
cached.forEach(h => onHighlight(h))
|
cached.forEach(h => onHighlight(h))
|
||||||
@@ -50,7 +49,6 @@ export const fetchHighlights = async (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(`📌 Fetched ${rawEvents.length} highlight events for author:`, pubkey.slice(0, 8))
|
|
||||||
|
|
||||||
// Store all events in event store if provided
|
// Store all events in event store if provided
|
||||||
if (eventStore) {
|
if (eventStore) {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export const fetchHighlightsForArticle = async (
|
|||||||
const cacheKey = highlightCache.articleKey(articleCoordinate)
|
const cacheKey = highlightCache.articleKey(articleCoordinate)
|
||||||
const cached = highlightCache.get(cacheKey)
|
const cached = highlightCache.get(cacheKey)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
console.log(`📌 Using cached highlights for article (${cached.length} items)`)
|
|
||||||
// Stream cached highlights if callback provided
|
// Stream cached highlights if callback provided
|
||||||
if (onHighlight) {
|
if (onHighlight) {
|
||||||
cached.forEach(h => onHighlight(h))
|
cached.forEach(h => onHighlight(h))
|
||||||
@@ -54,7 +53,6 @@ export const fetchHighlightsForArticle = async (
|
|||||||
])
|
])
|
||||||
|
|
||||||
const rawEvents = [...aTagEvents, ...eTagEvents]
|
const rawEvents = [...aTagEvents, ...eTagEvents]
|
||||||
console.log(`📌 Fetched ${rawEvents.length} highlight events for article:`, articleCoordinate)
|
|
||||||
|
|
||||||
// Store all events in event store if provided
|
// Store all events in event store if provided
|
||||||
if (eventStore) {
|
if (eventStore) {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const fetchHighlightsForUrl = async (
|
|||||||
const cacheKey = highlightCache.urlKey(url)
|
const cacheKey = highlightCache.urlKey(url)
|
||||||
const cached = highlightCache.get(cacheKey)
|
const cached = highlightCache.get(cacheKey)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
console.log(`📌 Using cached highlights for URL (${cached.length} items)`)
|
|
||||||
// Stream cached highlights if callback provided
|
// Stream cached highlights if callback provided
|
||||||
if (onHighlight) {
|
if (onHighlight) {
|
||||||
cached.forEach(h => onHighlight(h))
|
cached.forEach(h => onHighlight(h))
|
||||||
@@ -50,7 +49,6 @@ export const fetchHighlightsForUrl = async (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(`📌 Fetched ${rawEvents.length} highlight events for URL:`, url)
|
|
||||||
|
|
||||||
// Store all events in event store if provided
|
// Store all events in event store if provided
|
||||||
if (eventStore) {
|
if (eventStore) {
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ export const fetchHighlightsFromAuthors = async (
|
|||||||
): Promise<Highlight[]> => {
|
): Promise<Highlight[]> => {
|
||||||
try {
|
try {
|
||||||
if (pubkeys.length === 0) {
|
if (pubkeys.length === 0) {
|
||||||
console.log('⚠️ No pubkeys to fetch highlights from')
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('💡 Fetching highlights (kind 9802) from', pubkeys.length, 'authors')
|
|
||||||
|
|
||||||
const seenIds = new Set<string>()
|
const seenIds = new Set<string>()
|
||||||
const rawEvents = await queryEvents(
|
const rawEvents = await queryEvents(
|
||||||
@@ -55,7 +53,6 @@ export const fetchHighlightsFromAuthors = async (
|
|||||||
const uniqueEvents = dedupeHighlights(rawEvents)
|
const uniqueEvents = dedupeHighlights(rawEvents)
|
||||||
const highlights = uniqueEvents.map(eventToHighlight)
|
const highlights = uniqueEvents.map(eventToHighlight)
|
||||||
|
|
||||||
console.log('💡 Processed', highlights.length, 'unique highlights')
|
|
||||||
|
|
||||||
return sortHighlights(highlights)
|
return sortHighlights(highlights)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ class HighlightsController {
|
|||||||
|
|
||||||
// Skip if already loaded for this pubkey (unless forced)
|
// Skip if already loaded for this pubkey (unless forced)
|
||||||
if (!force && this.isLoadedFor(pubkey)) {
|
if (!force && this.isLoadedFor(pubkey)) {
|
||||||
console.log('[highlights] ✅ Already loaded for', pubkey.slice(0, 8))
|
|
||||||
this.emitHighlights(this.currentHighlights)
|
this.emitHighlights(this.currentHighlights)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -120,7 +119,6 @@ class HighlightsController {
|
|||||||
const currentGeneration = this.generation
|
const currentGeneration = this.generation
|
||||||
|
|
||||||
this.setLoading(true)
|
this.setLoading(true)
|
||||||
console.log('[highlights] 🔍 Loading highlights for', pubkey.slice(0, 8))
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const seenIds = new Set<string>()
|
const seenIds = new Set<string>()
|
||||||
@@ -134,7 +132,6 @@ class HighlightsController {
|
|||||||
}
|
}
|
||||||
if (lastSyncedAt) {
|
if (lastSyncedAt) {
|
||||||
filter.since = lastSyncedAt
|
filter.since = lastSyncedAt
|
||||||
console.log('[highlights] 📅 Incremental sync since', new Date(lastSyncedAt * 1000).toISOString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = await queryEvents(
|
const events = await queryEvents(
|
||||||
@@ -165,7 +162,6 @@ class HighlightsController {
|
|||||||
|
|
||||||
// Check if still active after async operation
|
// Check if still active after async operation
|
||||||
if (currentGeneration !== this.generation) {
|
if (currentGeneration !== this.generation) {
|
||||||
console.log('[highlights] ⚠️ Load cancelled (generation mismatch)')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +185,6 @@ class HighlightsController {
|
|||||||
this.setLastSyncedAt(pubkey, newestTimestamp)
|
this.setLastSyncedAt(pubkey, newestTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[highlights] ✅ Loaded', sorted.length, 'highlights')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[highlights] ❌ Failed to load highlights:', error)
|
console.error('[highlights] ❌ Failed to load highlights:', error)
|
||||||
this.currentHighlights = []
|
this.currentHighlights = []
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ const CACHE_NAME = 'boris-image-cache-v1'
|
|||||||
export async function clearImageCache(): Promise<void> {
|
export async function clearImageCache(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await caches.delete(CACHE_NAME)
|
await caches.delete(CACHE_NAME)
|
||||||
console.log('🗑️ Cleared all cached images')
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to clear image cache:', err)
|
console.error('Failed to clear image cache:', err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export async function fetchLinks(
|
|||||||
userPubkey: string,
|
userPubkey: string,
|
||||||
onItem?: (item: ReadItem) => void
|
onItem?: (item: ReadItem) => void
|
||||||
): Promise<ReadItem[]> {
|
): Promise<ReadItem[]> {
|
||||||
console.log('🔗 [Links] Fetching external links for user:', userPubkey.slice(0, 8))
|
|
||||||
|
|
||||||
const linksMap = new Map<string, ReadItem>()
|
const linksMap = new Map<string, ReadItem>()
|
||||||
|
|
||||||
@@ -37,7 +36,6 @@ export async function fetchLinks(
|
|||||||
fetchReadArticles(relayPool, userPubkey)
|
fetchReadArticles(relayPool, userPubkey)
|
||||||
])
|
])
|
||||||
|
|
||||||
console.log('📊 [Links] Data fetched:', {
|
|
||||||
readingProgress: progressEvents.length,
|
readingProgress: progressEvents.length,
|
||||||
markedAsRead: markedAsReadArticles.length
|
markedAsRead: markedAsReadArticles.length
|
||||||
})
|
})
|
||||||
@@ -79,7 +77,6 @@ export async function fetchLinks(
|
|||||||
const validLinks = filterValidItems(links)
|
const validLinks = filterValidItems(links)
|
||||||
const sortedLinks = sortByReadingActivity(validLinks)
|
const sortedLinks = sortByReadingActivity(validLinks)
|
||||||
|
|
||||||
console.log('✅ [Links] Processed', sortedLinks.length, 'total links')
|
|
||||||
return sortedLinks
|
return sortedLinks
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export const fetchNostrverseBlogPosts = async (
|
|||||||
onPost?: (post: BlogPostPreview) => void
|
onPost?: (post: BlogPostPreview) => void
|
||||||
): Promise<BlogPostPreview[]> => {
|
): Promise<BlogPostPreview[]> => {
|
||||||
try {
|
try {
|
||||||
console.log('[NOSTRVERSE] 📚 Fetching blog posts (kind 30023), limit:', limit)
|
|
||||||
|
|
||||||
// Deduplicate replaceable events by keeping the most recent version
|
// Deduplicate replaceable events by keeping the most recent version
|
||||||
const uniqueEvents = new Map<string, NostrEvent>()
|
const uniqueEvents = new Map<string, NostrEvent>()
|
||||||
@@ -63,7 +62,6 @@ export const fetchNostrverseBlogPosts = async (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('[NOSTRVERSE] 📊 Blog post events fetched (unique):', uniqueEvents.size)
|
|
||||||
|
|
||||||
// Convert to blog post previews and sort by published date (most recent first)
|
// Convert to blog post previews and sort by published date (most recent first)
|
||||||
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
|
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
|
||||||
@@ -81,7 +79,6 @@ export const fetchNostrverseBlogPosts = async (
|
|||||||
return timeB - timeA // Most recent first
|
return timeB - timeA // Most recent first
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('[NOSTRVERSE] 📰 Processed', blogPosts.length, 'unique blog posts')
|
|
||||||
|
|
||||||
return blogPosts
|
return blogPosts
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -103,7 +100,6 @@ export const fetchNostrverseHighlights = async (
|
|||||||
eventStore?: IEventStore
|
eventStore?: IEventStore
|
||||||
): Promise<Highlight[]> => {
|
): Promise<Highlight[]> => {
|
||||||
try {
|
try {
|
||||||
console.log('[NOSTRVERSE] 💡 Fetching highlights (kind 9802), limit:', limit)
|
|
||||||
|
|
||||||
const seenIds = new Set<string>()
|
const seenIds = new Set<string>()
|
||||||
// Collect but do not block callers awaiting network completion
|
// Collect but do not block callers awaiting network completion
|
||||||
@@ -133,7 +129,6 @@ export const fetchNostrverseHighlights = async (
|
|||||||
const uniqueEvents = dedupeHighlights([...collected, ...rawEvents])
|
const uniqueEvents = dedupeHighlights([...collected, ...rawEvents])
|
||||||
const highlights = uniqueEvents.map(eventToHighlight)
|
const highlights = uniqueEvents.map(eventToHighlight)
|
||||||
|
|
||||||
console.log('[NOSTRVERSE] 💡 Processed', highlights.length, 'unique highlights')
|
|
||||||
|
|
||||||
return sortHighlights(highlights)
|
return sortHighlights(highlights)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ const syncStateListeners: Array<(eventId: string, isSyncing: boolean) => void> =
|
|||||||
*/
|
*/
|
||||||
export function markEventAsOfflineCreated(eventId: string): void {
|
export function markEventAsOfflineCreated(eventId: string): void {
|
||||||
offlineCreatedEvents.add(eventId)
|
offlineCreatedEvents.add(eventId)
|
||||||
console.log(`📝 Marked event ${eventId.slice(0, 8)} as offline-created. Total: ${offlineCreatedEvents.size}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,49 +56,35 @@ export async function syncLocalEventsToRemote(
|
|||||||
eventStore: IEventStore
|
eventStore: IEventStore
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (isSyncing) {
|
if (isSyncing) {
|
||||||
console.log('⏳ Sync already in progress, skipping...')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔄 Coming back online - syncing local events to remote relays...')
|
|
||||||
console.log(`📦 Offline events tracked: ${offlineCreatedEvents.size}`)
|
|
||||||
isSyncing = true
|
isSyncing = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const remoteRelays = RELAYS.filter(url => !isLocalRelay(url))
|
const remoteRelays = RELAYS.filter(url => !isLocalRelay(url))
|
||||||
|
|
||||||
console.log(`📡 Remote relays: ${remoteRelays.length}`)
|
|
||||||
|
|
||||||
if (remoteRelays.length === 0) {
|
if (remoteRelays.length === 0) {
|
||||||
console.log('⚠️ No remote relays available for sync')
|
|
||||||
isSyncing = false
|
isSyncing = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offlineCreatedEvents.size === 0) {
|
if (offlineCreatedEvents.size === 0) {
|
||||||
console.log('✅ No offline events to sync')
|
|
||||||
isSyncing = false
|
isSyncing = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get events from EventStore using the tracked IDs
|
// Get events from EventStore using the tracked IDs
|
||||||
const eventsToSync: NostrEvent[] = []
|
const eventsToSync: NostrEvent[] = []
|
||||||
console.log(`🔍 Querying EventStore for ${offlineCreatedEvents.size} offline events...`)
|
|
||||||
|
|
||||||
for (const eventId of offlineCreatedEvents) {
|
for (const eventId of offlineCreatedEvents) {
|
||||||
const event = eventStore.getEvent(eventId)
|
const event = eventStore.getEvent(eventId)
|
||||||
if (event) {
|
if (event) {
|
||||||
console.log(`📥 Found event ${eventId.slice(0, 8)} (kind ${event.kind}) in EventStore`)
|
|
||||||
eventsToSync.push(event)
|
eventsToSync.push(event)
|
||||||
} else {
|
|
||||||
console.warn(`⚠️ Event ${eventId.slice(0, 8)} not found in EventStore`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`📊 Total events to sync: ${eventsToSync.length}`)
|
|
||||||
|
|
||||||
if (eventsToSync.length === 0) {
|
if (eventsToSync.length === 0) {
|
||||||
console.log('✅ No events found in EventStore to sync')
|
|
||||||
isSyncing = false
|
isSyncing = false
|
||||||
offlineCreatedEvents.clear()
|
offlineCreatedEvents.clear()
|
||||||
return
|
return
|
||||||
@@ -110,8 +95,6 @@ export async function syncLocalEventsToRemote(
|
|||||||
new Map(eventsToSync.map(e => [e.id, e])).values()
|
new Map(eventsToSync.map(e => [e.id, e])).values()
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(`📤 Syncing ${uniqueEvents.length} event(s) to remote relays...`)
|
|
||||||
|
|
||||||
// Mark all events as syncing
|
// Mark all events as syncing
|
||||||
uniqueEvents.forEach(event => {
|
uniqueEvents.forEach(event => {
|
||||||
syncingEvents.add(event.id)
|
syncingEvents.add(event.id)
|
||||||
@@ -127,13 +110,10 @@ export async function syncLocalEventsToRemote(
|
|||||||
await relayPool.publish(remoteRelays, event)
|
await relayPool.publish(remoteRelays, event)
|
||||||
successCount++
|
successCount++
|
||||||
successfulIds.push(event.id)
|
successfulIds.push(event.id)
|
||||||
console.log(`✅ Synced event ${event.id.slice(0, 8)}`)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`⚠️ Failed to sync event ${event.id.slice(0, 8)}:`, error)
|
// Silently fail for individual events
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✅ Synced ${successCount}/${uniqueEvents.length} events to remote relays`)
|
|
||||||
|
|
||||||
// Clear syncing state and offline tracking for successful events
|
// Clear syncing state and offline tracking for successful events
|
||||||
successfulIds.forEach(eventId => {
|
successfulIds.forEach(eventId => {
|
||||||
@@ -150,7 +130,7 @@ export async function syncLocalEventsToRemote(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error during offline sync:', error)
|
// Silently fail
|
||||||
} finally {
|
} finally {
|
||||||
isSyncing = false
|
isSyncing = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const fetchProfiles = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uniquePubkeys = Array.from(new Set(pubkeys))
|
const uniquePubkeys = Array.from(new Set(pubkeys))
|
||||||
console.log('👤 Fetching profiles (kind:0) for', uniquePubkeys.length, 'authors')
|
|
||||||
|
|
||||||
const relayUrls = Array.from(relayPool.relays.values()).map(relay => relay.url)
|
const relayUrls = Array.from(relayPool.relays.values()).map(relay => relay.url)
|
||||||
const prioritized = prioritizeLocalRelays(relayUrls)
|
const prioritized = prioritizeLocalRelays(relayUrls)
|
||||||
@@ -65,7 +64,6 @@ export const fetchProfiles = async (
|
|||||||
await lastValueFrom(merge(local$, remote$).pipe(toArray()))
|
await lastValueFrom(merge(local$, remote$).pipe(toArray()))
|
||||||
|
|
||||||
const profiles = Array.from(profilesByPubkey.values())
|
const profiles = Array.from(profilesByPubkey.values())
|
||||||
console.log('✅ Fetched', profiles.length, 'unique profiles')
|
|
||||||
|
|
||||||
// Rebroadcast profiles to local/all relays based on settings
|
// Rebroadcast profiles to local/all relays based on settings
|
||||||
if (profiles.length > 0) {
|
if (profiles.length > 0) {
|
||||||
|
|||||||
@@ -42,12 +42,10 @@ export async function createEventReaction(
|
|||||||
|
|
||||||
const signed = await factory.sign(draft)
|
const signed = await factory.sign(draft)
|
||||||
|
|
||||||
console.log('📚 Created kind:7 reaction (mark as read) for event:', eventId.slice(0, 8))
|
|
||||||
|
|
||||||
// Publish to relays
|
// Publish to relays
|
||||||
await relayPool.publish(RELAYS, signed)
|
await relayPool.publish(RELAYS, signed)
|
||||||
|
|
||||||
console.log('✅ Reaction published to', RELAYS.length, 'relay(s)')
|
|
||||||
|
|
||||||
return signed
|
return signed
|
||||||
}
|
}
|
||||||
@@ -94,12 +92,10 @@ export async function createWebsiteReaction(
|
|||||||
|
|
||||||
const signed = await factory.sign(draft)
|
const signed = await factory.sign(draft)
|
||||||
|
|
||||||
console.log('📚 Created kind:17 reaction (mark as read) for URL:', normalizedUrl)
|
|
||||||
|
|
||||||
// Publish to relays
|
// Publish to relays
|
||||||
await relayPool.publish(RELAYS, signed)
|
await relayPool.publish(RELAYS, signed)
|
||||||
|
|
||||||
console.log('✅ Website reaction published to', RELAYS.length, 'relay(s)')
|
|
||||||
|
|
||||||
return signed
|
return signed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,31 +27,23 @@ export function processReadingProgress(
|
|||||||
events: NostrEvent[],
|
events: NostrEvent[],
|
||||||
readsMap: Map<string, ReadItem>
|
readsMap: Map<string, ReadItem>
|
||||||
): void {
|
): void {
|
||||||
console.log('[progress] 🔧 processReadingProgress called with', events.length, 'events')
|
|
||||||
|
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
if (event.kind !== READING_PROGRESS_KIND) {
|
if (event.kind !== READING_PROGRESS_KIND) {
|
||||||
console.log('[progress] ⏭️ Skipping event with wrong kind:', event.kind)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const dTag = event.tags.find(t => t[0] === 'd')?.[1]
|
const dTag = event.tags.find(t => t[0] === 'd')?.[1]
|
||||||
if (!dTag) {
|
if (!dTag) {
|
||||||
console.log('[progress] ⚠️ Event missing d-tag:', event.id.slice(0, 8))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[progress] 📝 Processing event:', event.id.slice(0, 8), 'd-tag:', dTag.slice(0, 50))
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = JSON.parse(event.content)
|
const content = JSON.parse(event.content)
|
||||||
const position = content.progress || 0
|
const position = content.progress || 0
|
||||||
|
|
||||||
console.log('[progress] 📊 Progress value:', position, '(' + Math.round(position * 100) + '%)')
|
|
||||||
|
|
||||||
// Validate progress is between 0 and 1 (NIP-85 requirement)
|
// Validate progress is between 0 and 1 (NIP-85 requirement)
|
||||||
if (position < 0 || position > 1) {
|
if (position < 0 || position > 1) {
|
||||||
console.warn('[progress] ❌ Invalid progress value (must be 0-1):', position, 'event:', event.id.slice(0, 8))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,13 +68,10 @@ export function processReadingProgress(
|
|||||||
})
|
})
|
||||||
itemId = naddr
|
itemId = naddr
|
||||||
itemType = 'article'
|
itemType = 'article'
|
||||||
console.log('[progress] ✅ Converted coordinate to naddr:', naddr.slice(0, 50))
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[progress] ❌ Failed to encode naddr from coordinate:', dTag)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('[progress] ⚠️ Invalid coordinate format:', dTag)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if (dTag.startsWith('url:')) {
|
} else if (dTag.startsWith('url:')) {
|
||||||
@@ -92,13 +81,10 @@ export function processReadingProgress(
|
|||||||
itemUrl = atob(encoded.replace(/-/g, '+').replace(/_/g, '/'))
|
itemUrl = atob(encoded.replace(/-/g, '+').replace(/_/g, '/'))
|
||||||
itemId = itemUrl
|
itemId = itemUrl
|
||||||
itemType = 'external'
|
itemType = 'external'
|
||||||
console.log('[progress] ✅ Decoded URL:', itemUrl.slice(0, 50))
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[progress] ❌ Failed to decode URL from d tag:', dTag)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('[progress] ⚠️ Unknown d-tag format:', dTag)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,16 +100,11 @@ export function processReadingProgress(
|
|||||||
readingProgress: position,
|
readingProgress: position,
|
||||||
readingTimestamp: timestamp
|
readingTimestamp: timestamp
|
||||||
})
|
})
|
||||||
console.log('[progress] ✅ Added/updated item in readsMap:', itemId.slice(0, 50), '=', Math.round(position * 100) + '%')
|
|
||||||
} else {
|
|
||||||
console.log('[progress] ⏭️ Skipping older event for:', itemId.slice(0, 50))
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[progress] ❌ Failed to parse reading progress event:', error)
|
// Silently fail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[progress] 🏁 processReadingProgress finished, readsMap size:', readsMap.size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,14 +49,10 @@ function generateDTag(naddrOrUrl: string): string {
|
|||||||
const decoded = nip19.decode(naddrOrUrl)
|
const decoded = nip19.decode(naddrOrUrl)
|
||||||
if (decoded.type === 'naddr') {
|
if (decoded.type === 'naddr') {
|
||||||
const dTag = `${decoded.data.kind}:${decoded.data.pubkey}:${decoded.data.identifier || ''}`
|
const dTag = `${decoded.data.kind}:${decoded.data.pubkey}:${decoded.data.identifier || ''}`
|
||||||
console.log('[progress] 📋 Generated d-tag from naddr:', {
|
|
||||||
naddr: naddrOrUrl.slice(0, 50) + '...',
|
|
||||||
dTag: dTag.slice(0, 80) + '...'
|
|
||||||
})
|
|
||||||
return dTag
|
return dTag
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Failed to decode naddr:', naddrOrUrl)
|
// Ignore decode errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,14 +115,6 @@ export async function saveReadingPosition(
|
|||||||
articleIdentifier: string,
|
articleIdentifier: string,
|
||||||
position: ReadingPosition
|
position: ReadingPosition
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log('[progress] 💾 saveReadingPosition: Starting save:', {
|
|
||||||
identifier: articleIdentifier.slice(0, 50) + '...',
|
|
||||||
position: position.position,
|
|
||||||
positionPercent: Math.round(position.position * 100) + '%',
|
|
||||||
timestamp: position.timestamp,
|
|
||||||
scrollTop: position.scrollTop
|
|
||||||
})
|
|
||||||
|
|
||||||
const now = Math.floor(Date.now() / 1000)
|
const now = Math.floor(Date.now() / 1000)
|
||||||
|
|
||||||
const progressContent: ReadingProgressContent = {
|
const progressContent: ReadingProgressContent = {
|
||||||
@@ -138,13 +126,6 @@ export async function saveReadingPosition(
|
|||||||
|
|
||||||
const tags = generateProgressTags(articleIdentifier)
|
const tags = generateProgressTags(articleIdentifier)
|
||||||
|
|
||||||
console.log('[progress] 📝 Creating event with:', {
|
|
||||||
kind: READING_PROGRESS_KIND,
|
|
||||||
content: progressContent,
|
|
||||||
tags: tags.map(t => `[${t.join(', ')}]`).join(', '),
|
|
||||||
created_at: now
|
|
||||||
})
|
|
||||||
|
|
||||||
const draft = await factory.create(async () => ({
|
const draft = await factory.create(async () => ({
|
||||||
kind: READING_PROGRESS_KIND,
|
kind: READING_PROGRESS_KIND,
|
||||||
content: JSON.stringify(progressContent),
|
content: JSON.stringify(progressContent),
|
||||||
@@ -152,20 +133,9 @@ export async function saveReadingPosition(
|
|||||||
created_at: now
|
created_at: now
|
||||||
}))
|
}))
|
||||||
|
|
||||||
console.log('[progress] ✍️ Signing event...')
|
|
||||||
const signed = await factory.sign(draft)
|
const signed = await factory.sign(draft)
|
||||||
|
|
||||||
console.log('[progress] 📡 Publishing event:', {
|
|
||||||
id: signed.id,
|
|
||||||
kind: signed.kind,
|
|
||||||
pubkey: signed.pubkey.slice(0, 8) + '...',
|
|
||||||
content: signed.content,
|
|
||||||
tags: signed.tags
|
|
||||||
})
|
|
||||||
|
|
||||||
await publishEvent(relayPool, eventStore, signed)
|
await publishEvent(relayPool, eventStore, signed)
|
||||||
|
|
||||||
console.log('[progress] ✅ Event published successfully, ID:', signed.id.slice(0, 16))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,12 +149,6 @@ export async function loadReadingPosition(
|
|||||||
): Promise<ReadingPosition | null> {
|
): Promise<ReadingPosition | null> {
|
||||||
const dTag = generateDTag(articleIdentifier)
|
const dTag = generateDTag(articleIdentifier)
|
||||||
|
|
||||||
console.log('📖 [ReadingProgress] Loading position:', {
|
|
||||||
pubkey: pubkey.slice(0, 8) + '...',
|
|
||||||
identifier: articleIdentifier.slice(0, 32) + '...',
|
|
||||||
dTag: dTag.slice(0, 50) + '...'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Check local event store first
|
// Check local event store first
|
||||||
try {
|
try {
|
||||||
const localEvent = await firstValueFrom(
|
const localEvent = await firstValueFrom(
|
||||||
@@ -193,12 +157,6 @@ export async function loadReadingPosition(
|
|||||||
if (localEvent) {
|
if (localEvent) {
|
||||||
const content = getReadingProgressContent(localEvent)
|
const content = getReadingProgressContent(localEvent)
|
||||||
if (content) {
|
if (content) {
|
||||||
console.log('✅ [ReadingProgress] Loaded from local store:', {
|
|
||||||
position: content.position,
|
|
||||||
positionPercent: Math.round(content.position * 100) + '%',
|
|
||||||
timestamp: content.timestamp
|
|
||||||
})
|
|
||||||
|
|
||||||
// Fetch from relays in background to get any updates
|
// Fetch from relays in background to get any updates
|
||||||
relayPool
|
relayPool
|
||||||
.subscription(RELAYS, {
|
.subscription(RELAYS, {
|
||||||
@@ -213,7 +171,7 @@ export async function loadReadingPosition(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('📭 No cached reading progress found, fetching from relays...')
|
// Ignore errors and fetch from relays
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch from relays
|
// Fetch from relays
|
||||||
@@ -226,13 +184,7 @@ export async function loadReadingPosition(
|
|||||||
getReadingProgressContent
|
getReadingProgressContent
|
||||||
)
|
)
|
||||||
|
|
||||||
if (result) {
|
return result || null
|
||||||
console.log('✅ [ReadingProgress] Loaded from relays')
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('📭 No reading progress found')
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to fetch from relays with timeout
|
// Helper function to fetch from relays with timeout
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class ReadingProgressController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private emitProgress(progressMap: Map<string, number>): void {
|
private emitProgress(progressMap: Map<string, number>): void {
|
||||||
console.log('[progress] 📡 Emitting to', this.progressListeners.length, 'listeners with', progressMap.size, 'items')
|
|
||||||
this.progressListeners.forEach(cb => cb(new Map(progressMap)))
|
this.progressListeners.forEach(cb => cb(new Map(progressMap)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ class ReadingProgressController {
|
|||||||
parsed[pubkey] = Object.fromEntries(progressMap.entries())
|
parsed[pubkey] = Object.fromEntries(progressMap.entries())
|
||||||
localStorage.setItem(PROGRESS_CACHE_KEY, JSON.stringify(parsed))
|
localStorage.setItem(PROGRESS_CACHE_KEY, JSON.stringify(parsed))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[progress] ⚠️ Failed to persist reading progress cache:', err)
|
// Silently fail cache persistence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +108,7 @@ class ReadingProgressController {
|
|||||||
try {
|
try {
|
||||||
this.timelineSubscription.unsubscribe()
|
this.timelineSubscription.unsubscribe()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[progress] ⚠️ Failed to unsubscribe timeline on reset:', err)
|
// Silently fail on unsubscribe
|
||||||
}
|
}
|
||||||
this.timelineSubscription = null
|
this.timelineSubscription = null
|
||||||
}
|
}
|
||||||
@@ -142,7 +141,7 @@ class ReadingProgressController {
|
|||||||
parsed[pubkey] = timestamp
|
parsed[pubkey] = timestamp
|
||||||
localStorage.setItem(LAST_SYNCED_KEY, JSON.stringify(parsed))
|
localStorage.setItem(LAST_SYNCED_KEY, JSON.stringify(parsed))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Failed to update last synced timestamp:', err)
|
// Silently fail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,12 +159,9 @@ class ReadingProgressController {
|
|||||||
|
|
||||||
// Skip if already loaded for this pubkey and not forcing
|
// Skip if already loaded for this pubkey and not forcing
|
||||||
if (!force && this.isLoadedFor(pubkey)) {
|
if (!force && this.isLoadedFor(pubkey)) {
|
||||||
console.log('📊 [ReadingProgress] Already loaded for', pubkey.slice(0, 8))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📊 [ReadingProgress] Loading for', pubkey.slice(0, 8), force ? '(forced)' : '')
|
|
||||||
|
|
||||||
this.setLoading(true)
|
this.setLoading(true)
|
||||||
this.lastLoadedPubkey = pubkey
|
this.lastLoadedPubkey = pubkey
|
||||||
|
|
||||||
@@ -173,7 +169,6 @@ class ReadingProgressController {
|
|||||||
// Seed from local cache immediately (survives refresh/flight mode)
|
// Seed from local cache immediately (survives refresh/flight mode)
|
||||||
const cached = this.loadCachedProgress(pubkey)
|
const cached = this.loadCachedProgress(pubkey)
|
||||||
if (cached.size > 0) {
|
if (cached.size > 0) {
|
||||||
console.log('📊 [ReadingProgress] Seeded from cache:', cached.size, 'items')
|
|
||||||
this.currentProgressMap = cached
|
this.currentProgressMap = cached
|
||||||
this.emitProgress(this.currentProgressMap)
|
this.emitProgress(this.currentProgressMap)
|
||||||
}
|
}
|
||||||
@@ -184,7 +179,7 @@ class ReadingProgressController {
|
|||||||
try {
|
try {
|
||||||
this.timelineSubscription.unsubscribe()
|
this.timelineSubscription.unsubscribe()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[progress] ⚠️ Failed to unsubscribe previous timeline:', err)
|
// Silently fail
|
||||||
}
|
}
|
||||||
this.timelineSubscription = null
|
this.timelineSubscription = null
|
||||||
}
|
}
|
||||||
@@ -198,7 +193,6 @@ class ReadingProgressController {
|
|||||||
// Ignore if controller generation has changed (e.g., logout/login)
|
// Ignore if controller generation has changed (e.g., logout/login)
|
||||||
if (generationAtSubscribe !== this.generation) return
|
if (generationAtSubscribe !== this.generation) return
|
||||||
if (!Array.isArray(localEvents) || localEvents.length === 0) return
|
if (!Array.isArray(localEvents) || localEvents.length === 0) return
|
||||||
console.log('📊 [ReadingProgress] Timeline update with', localEvents.length, 'event(s)')
|
|
||||||
this.processEvents(localEvents)
|
this.processEvents(localEvents)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -214,15 +208,11 @@ class ReadingProgressController {
|
|||||||
|
|
||||||
if (lastSynced && !needsFullSync) {
|
if (lastSynced && !needsFullSync) {
|
||||||
filter.since = lastSynced
|
filter.since = lastSynced
|
||||||
console.log('📊 [ReadingProgress] Incremental sync since', new Date(lastSynced * 1000).toISOString())
|
|
||||||
} else {
|
|
||||||
console.log('📊 [ReadingProgress] Full sync (map size:', this.currentProgressMap.size + ')')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const relayEvents = await queryEvents(relayPool, filter, { relayUrls: RELAYS })
|
const relayEvents = await queryEvents(relayPool, filter, { relayUrls: RELAYS })
|
||||||
|
|
||||||
if (startGeneration !== this.generation) {
|
if (startGeneration !== this.generation) {
|
||||||
console.log('📊 [ReadingProgress] Cancelled (generation changed)')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,13 +222,10 @@ class ReadingProgressController {
|
|||||||
|
|
||||||
// Process and emit (merge with existing)
|
// Process and emit (merge with existing)
|
||||||
this.processEvents(relayEvents)
|
this.processEvents(relayEvents)
|
||||||
console.log('📊 [ReadingProgress] Loaded', relayEvents.length, 'events from relays')
|
|
||||||
|
|
||||||
// Update last synced
|
// Update last synced
|
||||||
const now = Math.floor(Date.now() / 1000)
|
const now = Math.floor(Date.now() / 1000)
|
||||||
this.updateLastSyncedAt(pubkey, now)
|
this.updateLastSyncedAt(pubkey, now)
|
||||||
} else {
|
|
||||||
console.log('📊 [ReadingProgress] No new events from relays')
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('📊 [ReadingProgress] Failed to load:', err)
|
console.error('📊 [ReadingProgress] Failed to load:', err)
|
||||||
@@ -253,8 +240,6 @@ class ReadingProgressController {
|
|||||||
* Process events and update progress map
|
* Process events and update progress map
|
||||||
*/
|
*/
|
||||||
private processEvents(events: NostrEvent[]): void {
|
private processEvents(events: NostrEvent[]): void {
|
||||||
console.log('[progress] 🔄 Processing', events.length, 'events')
|
|
||||||
|
|
||||||
const readsMap = new Map<string, ReadItem>()
|
const readsMap = new Map<string, ReadItem>()
|
||||||
|
|
||||||
// Merge with existing progress
|
// Merge with existing progress
|
||||||
@@ -267,24 +252,17 @@ class ReadingProgressController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[progress] 📦 Starting with', readsMap.size, 'existing items')
|
|
||||||
|
|
||||||
// Process new events
|
// Process new events
|
||||||
processReadingProgress(events, readsMap)
|
processReadingProgress(events, readsMap)
|
||||||
|
|
||||||
console.log('[progress] 📦 After processing:', readsMap.size, 'items')
|
|
||||||
|
|
||||||
// Convert back to progress map (naddr -> progress)
|
// Convert back to progress map (naddr -> progress)
|
||||||
const newProgressMap = new Map<string, number>()
|
const newProgressMap = new Map<string, number>()
|
||||||
for (const [id, item] of readsMap.entries()) {
|
for (const [id, item] of readsMap.entries()) {
|
||||||
if (item.readingProgress !== undefined && item.type === 'article') {
|
if (item.readingProgress !== undefined && item.type === 'article') {
|
||||||
newProgressMap.set(id, item.readingProgress)
|
newProgressMap.set(id, item.readingProgress)
|
||||||
console.log('[progress] ✅ Added:', id.slice(0, 50) + '...', '=', Math.round(item.readingProgress * 100) + '%')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[progress] 📊 Final progress map size:', newProgressMap.size)
|
|
||||||
|
|
||||||
this.currentProgressMap = newProgressMap
|
this.currentProgressMap = newProgressMap
|
||||||
this.emitProgress(this.currentProgressMap)
|
this.emitProgress(this.currentProgressMap)
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export async function fetchAllReads(
|
|||||||
bookmarks: Bookmark[],
|
bookmarks: Bookmark[],
|
||||||
onItem?: (item: ReadItem) => void
|
onItem?: (item: ReadItem) => void
|
||||||
): Promise<ReadItem[]> {
|
): Promise<ReadItem[]> {
|
||||||
console.log('📚 [Reads] Fetching all reads for user:', userPubkey.slice(0, 8))
|
|
||||||
|
|
||||||
const readsMap = new Map<string, ReadItem>()
|
const readsMap = new Map<string, ReadItem>()
|
||||||
|
|
||||||
@@ -66,7 +65,6 @@ export async function fetchAllReads(
|
|||||||
fetchReadArticles(relayPool, userPubkey)
|
fetchReadArticles(relayPool, userPubkey)
|
||||||
])
|
])
|
||||||
|
|
||||||
console.log('📊 [Reads] Data fetched:', {
|
|
||||||
readingProgress: progressEvents.length,
|
readingProgress: progressEvents.length,
|
||||||
markedAsRead: markedAsReadArticles.length,
|
markedAsRead: markedAsReadArticles.length,
|
||||||
bookmarks: bookmarks.length
|
bookmarks: bookmarks.length
|
||||||
@@ -120,7 +118,6 @@ export async function fetchAllReads(
|
|||||||
.map(item => item.id)
|
.map(item => item.id)
|
||||||
|
|
||||||
if (articleCoordinates.length > 0) {
|
if (articleCoordinates.length > 0) {
|
||||||
console.log('📖 [Reads] Fetching article events for', articleCoordinates.length, 'articles')
|
|
||||||
|
|
||||||
// Parse coordinates and fetch events
|
// Parse coordinates and fetch events
|
||||||
const articlesToFetch: Array<{ pubkey: string; identifier: string }> = []
|
const articlesToFetch: Array<{ pubkey: string; identifier: string }> = []
|
||||||
@@ -187,7 +184,6 @@ export async function fetchAllReads(
|
|||||||
const validArticles = filterValidItems(articles)
|
const validArticles = filterValidItems(articles)
|
||||||
const sortedReads = sortByReadingActivity(validArticles)
|
const sortedReads = sortByReadingActivity(validArticles)
|
||||||
|
|
||||||
console.log('✅ [Reads] Processed', sortedReads.length, 'total reads')
|
|
||||||
return sortedReads
|
return sortedReads
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export async function rebroadcastEvents(
|
|||||||
|
|
||||||
// If we're in flight mode (only local relays connected) and user wants to broadcast to all relays, skip
|
// If we're in flight mode (only local relays connected) and user wants to broadcast to all relays, skip
|
||||||
if (broadcastToAll && !hasRemoteConnection) {
|
if (broadcastToAll && !hasRemoteConnection) {
|
||||||
console.log('✈️ Flight mode: skipping rebroadcast to remote relays')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +49,6 @@ export async function rebroadcastEvents(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetRelays.length === 0) {
|
if (targetRelays.length === 0) {
|
||||||
console.log('📡 No target relays for rebroadcast')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +56,6 @@ export async function rebroadcastEvents(
|
|||||||
const rebroadcastPromises = events.map(async (event) => {
|
const rebroadcastPromises = events.map(async (event) => {
|
||||||
try {
|
try {
|
||||||
await relayPool.publish(targetRelays, event)
|
await relayPool.publish(targetRelays, event)
|
||||||
console.log('📡 Rebroadcast event', event.id?.slice(0, 8), 'to', targetRelays.length, 'relay(s)')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Failed to rebroadcast event', event.id?.slice(0, 8), error)
|
console.warn('⚠️ Failed to rebroadcast event', event.id?.slice(0, 8), error)
|
||||||
}
|
}
|
||||||
@@ -69,7 +66,6 @@ export async function rebroadcastEvents(
|
|||||||
console.warn('⚠️ Some rebroadcasts failed:', err)
|
console.warn('⚠️ Some rebroadcasts failed:', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`📡 Rebroadcasting ${events.length} event(s) to ${targetRelays.length} relay(s)`, {
|
|
||||||
broadcastToAll,
|
broadcastToAll,
|
||||||
useLocalCache,
|
useLocalCache,
|
||||||
targetRelays
|
targetRelays
|
||||||
|
|||||||
@@ -40,11 +40,8 @@ export function updateAndGetRelayStatuses(relayPool: RelayPool): RelayStatus[] {
|
|||||||
const connectedCount = statuses.filter(s => s.isInPool).length
|
const connectedCount = statuses.filter(s => s.isInPool).length
|
||||||
const disconnectedCount = statuses.filter(s => !s.isInPool).length
|
const disconnectedCount = statuses.filter(s => !s.isInPool).length
|
||||||
if (connectedCount === 0 || disconnectedCount > 0) {
|
if (connectedCount === 0 || disconnectedCount > 0) {
|
||||||
console.log(`🔌 Relay status: ${connectedCount} connected, ${disconnectedCount} disconnected`)
|
|
||||||
const connected = statuses.filter(s => s.isInPool).map(s => s.url.replace(/^wss?:\/\//, ''))
|
const connected = statuses.filter(s => s.isInPool).map(s => s.url.replace(/^wss?:\/\//, ''))
|
||||||
const disconnected = statuses.filter(s => !s.isInPool).map(s => s.url.replace(/^wss?:\/\//, ''))
|
const disconnected = statuses.filter(s => !s.isInPool).map(s => s.url.replace(/^wss?:\/\//, ''))
|
||||||
if (connected.length > 0) console.log('✅ Connected:', connected.join(', '))
|
|
||||||
if (disconnected.length > 0) console.log('❌ Disconnected:', disconnected.join(', '))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add recently seen relays that are no longer connected
|
// Add recently seen relays that are no longer connected
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export async function loadSettings(
|
|||||||
pubkey: string,
|
pubkey: string,
|
||||||
relays: string[]
|
relays: string[]
|
||||||
): Promise<UserSettings | null> {
|
): Promise<UserSettings | null> {
|
||||||
console.log('⚙️ Loading settings from nostr...', { pubkey: pubkey.slice(0, 8) + '...', relays })
|
|
||||||
|
|
||||||
// First, check if we already have settings in the local event store
|
// First, check if we already have settings in the local event store
|
||||||
try {
|
try {
|
||||||
@@ -80,7 +79,6 @@ export async function loadSettings(
|
|||||||
)
|
)
|
||||||
if (localEvent) {
|
if (localEvent) {
|
||||||
const content = getAppDataContent<UserSettings>(localEvent)
|
const content = getAppDataContent<UserSettings>(localEvent)
|
||||||
console.log('✅ Settings loaded from local store (cached):', content)
|
|
||||||
|
|
||||||
// Still fetch from relays in the background to get any updates
|
// Still fetch from relays in the background to get any updates
|
||||||
relayPool
|
relayPool
|
||||||
@@ -95,7 +93,6 @@ export async function loadSettings(
|
|||||||
return content || null
|
return content || null
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('📭 No cached settings found, fetching from relays...')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not in local store, fetch from relays
|
// If not in local store, fetch from relays
|
||||||
@@ -127,10 +124,8 @@ export async function loadSettings(
|
|||||||
)
|
)
|
||||||
if (event) {
|
if (event) {
|
||||||
const content = getAppDataContent<UserSettings>(event)
|
const content = getAppDataContent<UserSettings>(event)
|
||||||
console.log('✅ Settings loaded from relays:', content)
|
|
||||||
resolve(content || null)
|
resolve(content || null)
|
||||||
} else {
|
} else {
|
||||||
console.log('📭 No settings event found - using defaults')
|
|
||||||
resolve(null)
|
resolve(null)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -161,7 +156,6 @@ export async function saveSettings(
|
|||||||
factory: EventFactory,
|
factory: EventFactory,
|
||||||
settings: UserSettings
|
settings: UserSettings
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log('💾 Saving settings to nostr:', settings)
|
|
||||||
|
|
||||||
// Create NIP-78 application data event manually
|
// Create NIP-78 application data event manually
|
||||||
// Note: AppDataBlueprint is not available in the npm package
|
// Note: AppDataBlueprint is not available in the npm package
|
||||||
@@ -177,7 +171,6 @@ export async function saveSettings(
|
|||||||
// Use unified write service
|
// Use unified write service
|
||||||
await publishEvent(relayPool, eventStore, signed)
|
await publishEvent(relayPool, eventStore, signed)
|
||||||
|
|
||||||
console.log('✅ Settings published successfully')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function watchSettings(
|
export function watchSettings(
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ export async function createWebBookmark(
|
|||||||
// Publish to relays in the background (don't block UI)
|
// Publish to relays in the background (don't block UI)
|
||||||
relayPool.publish(relays, signedEvent)
|
relayPool.publish(relays, signedEvent)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('✅ Web bookmark published to', relays.length, 'relays:', signedEvent)
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn('⚠️ Some relays failed to publish bookmark:', err)
|
console.warn('⚠️ Some relays failed to publish bookmark:', err)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export async function publishEvent(
|
|||||||
|
|
||||||
// Store the event in the local EventStore FIRST for immediate UI display
|
// Store the event in the local EventStore FIRST for immediate UI display
|
||||||
eventStore.add(event)
|
eventStore.add(event)
|
||||||
console.log(`${logPrefix} 💾 Stored event in EventStore:`, event.id.slice(0, 8), `(kind ${event.kind})`)
|
|
||||||
|
|
||||||
// Check current connection status - are we online or in flight mode?
|
// Check current connection status - are we online or in flight mode?
|
||||||
const connectedRelays = Array.from(relayPool.relays.values())
|
const connectedRelays = Array.from(relayPool.relays.values())
|
||||||
@@ -35,7 +34,6 @@ export async function publishEvent(
|
|||||||
|
|
||||||
const isLocalOnly = areAllRelaysLocal(expectedSuccessRelays)
|
const isLocalOnly = areAllRelaysLocal(expectedSuccessRelays)
|
||||||
|
|
||||||
console.log(`${logPrefix} 📍 Event relay status:`, {
|
|
||||||
targetRelays: RELAYS.length,
|
targetRelays: RELAYS.length,
|
||||||
expectedSuccessRelays: expectedSuccessRelays.length,
|
expectedSuccessRelays: expectedSuccessRelays.length,
|
||||||
isLocalOnly,
|
isLocalOnly,
|
||||||
@@ -50,10 +48,8 @@ export async function publishEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish to all configured relays in the background (non-blocking)
|
// Publish to all configured relays in the background (non-blocking)
|
||||||
console.log(`${logPrefix} 📤 Publishing to relays:`, RELAYS)
|
|
||||||
relayPool.publish(RELAYS, event)
|
relayPool.publish(RELAYS, event)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log(`${logPrefix} ✅ Event published to`, RELAYS.length, 'relay(s):', event.id.slice(0, 8))
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn(`${logPrefix} ⚠️ Failed to publish event to relays (event still saved locally):`, error)
|
console.warn(`${logPrefix} ⚠️ Failed to publish event to relays (event still saved locally):`, error)
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ class WritingsController {
|
|||||||
|
|
||||||
// Skip if already loaded for this pubkey (unless forced)
|
// Skip if already loaded for this pubkey (unless forced)
|
||||||
if (!force && this.isLoadedFor(pubkey)) {
|
if (!force && this.isLoadedFor(pubkey)) {
|
||||||
console.log('[writings] ✅ Already loaded for', pubkey.slice(0, 8))
|
|
||||||
this.emitWritings(this.currentPosts)
|
this.emitWritings(this.currentPosts)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -148,7 +147,6 @@ class WritingsController {
|
|||||||
const currentGeneration = this.generation
|
const currentGeneration = this.generation
|
||||||
|
|
||||||
this.setLoading(true)
|
this.setLoading(true)
|
||||||
console.log('[writings] 🔍 Loading writings for', pubkey.slice(0, 8))
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const seenIds = new Set<string>()
|
const seenIds = new Set<string>()
|
||||||
@@ -162,7 +160,6 @@ class WritingsController {
|
|||||||
}
|
}
|
||||||
if (lastSyncedAt) {
|
if (lastSyncedAt) {
|
||||||
filter.since = lastSyncedAt
|
filter.since = lastSyncedAt
|
||||||
console.log('[writings] 📅 Incremental sync since', new Date(lastSyncedAt * 1000).toISOString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = await queryEvents(
|
const events = await queryEvents(
|
||||||
@@ -201,7 +198,6 @@ class WritingsController {
|
|||||||
|
|
||||||
// Check if still active after async operation
|
// Check if still active after async operation
|
||||||
if (currentGeneration !== this.generation) {
|
if (currentGeneration !== this.generation) {
|
||||||
console.log('[writings] ⚠️ Load cancelled (generation mismatch)')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +227,6 @@ class WritingsController {
|
|||||||
this.setLastSyncedAt(pubkey, newestTimestamp)
|
this.setLastSyncedAt(pubkey, newestTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[writings] ✅ Loaded', sorted.length, 'writings')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[writings] ❌ Failed to load writings:', error)
|
console.error('[writings] ❌ Failed to load writings:', error)
|
||||||
this.currentPosts = []
|
this.currentPosts = []
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export async function fetchBorisZappers(
|
|||||||
relayPool: RelayPool
|
relayPool: RelayPool
|
||||||
): Promise<ZapSender[]> {
|
): Promise<ZapSender[]> {
|
||||||
try {
|
try {
|
||||||
console.log('⚡ Fetching zap receipts for Boris...', BORIS_PUBKEY)
|
|
||||||
|
|
||||||
// Use all configured relays plus specific zap-heavy relays
|
// Use all configured relays plus specific zap-heavy relays
|
||||||
const zapRelays = [
|
const zapRelays = [
|
||||||
@@ -63,7 +62,6 @@ export async function fetchBorisZappers(
|
|||||||
merge(local$, remote$).pipe(toArray())
|
merge(local$, remote$).pipe(toArray())
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(`📊 Fetched ${zapReceipts.length} raw zap receipts`)
|
|
||||||
|
|
||||||
// Dedupe by event ID and validate
|
// Dedupe by event ID and validate
|
||||||
const uniqueReceipts = new Map<string, NostrEvent>()
|
const uniqueReceipts = new Map<string, NostrEvent>()
|
||||||
@@ -79,7 +77,6 @@ export async function fetchBorisZappers(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`✅ ${uniqueReceipts.size} valid zap receipts (${invalidCount} invalid)`)
|
|
||||||
|
|
||||||
// Aggregate by sender using applesauce helpers
|
// Aggregate by sender using applesauce helpers
|
||||||
const senderTotals = new Map<string, { totalSats: number; zapCount: number }>()
|
const senderTotals = new Map<string, { totalSats: number; zapCount: number }>()
|
||||||
@@ -102,7 +99,6 @@ export async function fetchBorisZappers(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`👥 Found ${senderTotals.size} unique senders`)
|
|
||||||
|
|
||||||
// Filter >= 2100 sats, mark whales >= 69420 sats, sort by total desc
|
// Filter >= 2100 sats, mark whales >= 69420 sats, sort by total desc
|
||||||
const zappers: ZapSender[] = Array.from(senderTotals.entries())
|
const zappers: ZapSender[] = Array.from(senderTotals.entries())
|
||||||
@@ -115,7 +111,6 @@ export async function fetchBorisZappers(
|
|||||||
}))
|
}))
|
||||||
.sort((a, b) => b.totalSats - a.totalSats)
|
.sort((a, b) => b.totalSats - a.totalSats)
|
||||||
|
|
||||||
console.log(`✅ Found ${zappers.length} supporters (${zappers.filter(z => z.isWhale).length} whales)`)
|
|
||||||
|
|
||||||
return zappers
|
return zappers
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ cleanupOutdatedCaches()
|
|||||||
sw.skipWaiting()
|
sw.skipWaiting()
|
||||||
clientsClaim()
|
clientsClaim()
|
||||||
|
|
||||||
console.log('[SW] Boris service worker loaded')
|
|
||||||
|
|
||||||
// Runtime cache: Cross-origin images
|
// Runtime cache: Cross-origin images
|
||||||
// This preserves the existing image caching behavior
|
// This preserves the existing image caching behavior
|
||||||
|
|||||||
@@ -16,18 +16,15 @@ const loadingFonts = new Map<string, Promise<void>>()
|
|||||||
|
|
||||||
export async function loadFont(fontKey: string): Promise<void> {
|
export async function loadFont(fontKey: string): Promise<void> {
|
||||||
if (fontKey === 'system') {
|
if (fontKey === 'system') {
|
||||||
console.log('📝 Using system font')
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadedFonts.has(fontKey)) {
|
if (loadedFonts.has(fontKey)) {
|
||||||
console.log('✅ Font already loaded:', fontKey)
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If font is currently loading, return the existing promise
|
// If font is currently loading, return the existing promise
|
||||||
if (loadingFonts.has(fontKey)) {
|
if (loadingFonts.has(fontKey)) {
|
||||||
console.log('⏳ Font already loading:', fontKey)
|
|
||||||
return loadingFonts.get(fontKey)!
|
return loadingFonts.get(fontKey)!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +34,6 @@ export async function loadFont(fontKey: string): Promise<void> {
|
|||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔤 Loading font:', fontFamily)
|
|
||||||
|
|
||||||
// Create a promise for this font loading
|
// Create a promise for this font loading
|
||||||
const loadPromise = new Promise<void>((resolve) => {
|
const loadPromise = new Promise<void>((resolve) => {
|
||||||
@@ -48,7 +44,6 @@ export async function loadFont(fontKey: string): Promise<void> {
|
|||||||
|
|
||||||
// Wait for the stylesheet to load
|
// Wait for the stylesheet to load
|
||||||
link.onload = () => {
|
link.onload = () => {
|
||||||
console.log('📄 Stylesheet loaded for:', fontFamily)
|
|
||||||
|
|
||||||
// Use Font Loading API to wait for the actual font to be ready
|
// Use Font Loading API to wait for the actual font to be ready
|
||||||
if ('fonts' in document) {
|
if ('fonts' in document) {
|
||||||
@@ -56,7 +51,6 @@ export async function loadFont(fontKey: string): Promise<void> {
|
|||||||
document.fonts.load(`400 16px "${fontFamily}"`),
|
document.fonts.load(`400 16px "${fontFamily}"`),
|
||||||
document.fonts.load(`700 16px "${fontFamily}"`)
|
document.fonts.load(`700 16px "${fontFamily}"`)
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
console.log('✅ Font ready:', fontFamily)
|
|
||||||
loadedFonts.add(fontKey)
|
loadedFonts.add(fontKey)
|
||||||
loadingFonts.delete(fontKey)
|
loadingFonts.delete(fontKey)
|
||||||
resolve()
|
resolve()
|
||||||
@@ -69,7 +63,6 @@ export async function loadFont(fontKey: string): Promise<void> {
|
|||||||
} else {
|
} else {
|
||||||
// Fallback: just wait a bit for older browsers
|
// Fallback: just wait a bit for older browsers
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('✅ Font assumed ready (no Font Loading API):', fontFamily)
|
|
||||||
loadedFonts.add(fontKey)
|
loadedFonts.add(fontKey)
|
||||||
loadingFonts.delete(fontKey)
|
loadingFonts.delete(fontKey)
|
||||||
resolve()
|
resolve()
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ export function applyHighlightsToHTML(
|
|||||||
highlightStyle: 'marker' | 'underline' = 'marker'
|
highlightStyle: 'marker' | 'underline' = 'marker'
|
||||||
): string {
|
): string {
|
||||||
if (!html || highlights.length === 0) {
|
if (!html || highlights.length === 0) {
|
||||||
console.log('⚠️ applyHighlightsToHTML: No HTML or highlights', {
|
|
||||||
htmlLength: html?.length,
|
htmlLength: html?.length,
|
||||||
highlightsCount: highlights.length
|
highlightsCount: highlights.length
|
||||||
})
|
})
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔨 applyHighlightsToHTML: Processing', highlights.length, 'highlights')
|
|
||||||
|
|
||||||
const tempDiv = document.createElement('div')
|
const tempDiv = document.createElement('div')
|
||||||
tempDiv.innerHTML = html
|
tempDiv.innerHTML = html
|
||||||
@@ -31,7 +29,6 @@ export function applyHighlightsToHTML(
|
|||||||
mark.parentNode?.replaceChild(textNode, mark)
|
mark.parentNode?.replaceChild(textNode, mark)
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('🧹 Removed', existingMarks.length, 'existing highlight marks')
|
|
||||||
|
|
||||||
let appliedCount = 0
|
let appliedCount = 0
|
||||||
|
|
||||||
@@ -42,7 +39,6 @@ export function applyHighlightsToHTML(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔍 Searching for highlight:', searchText.substring(0, 50) + '...')
|
|
||||||
|
|
||||||
// Collect all text nodes
|
// Collect all text nodes
|
||||||
const walker = document.createTreeWalker(tempDiv, NodeFilter.SHOW_TEXT, null)
|
const walker = document.createTreeWalker(tempDiv, NodeFilter.SHOW_TEXT, null)
|
||||||
@@ -50,7 +46,6 @@ export function applyHighlightsToHTML(
|
|||||||
let node: Node | null
|
let node: Node | null
|
||||||
while ((node = walker.nextNode())) textNodes.push(node as Text)
|
while ((node = walker.nextNode())) textNodes.push(node as Text)
|
||||||
|
|
||||||
console.log('📄 Found', textNodes.length, 'text nodes to search')
|
|
||||||
|
|
||||||
// Try exact match first, then normalized match
|
// Try exact match first, then normalized match
|
||||||
const found = tryMarkInTextNodes(textNodes, searchText, highlight, false, highlightStyle) ||
|
const found = tryMarkInTextNodes(textNodes, searchText, highlight, false, highlightStyle) ||
|
||||||
@@ -58,13 +53,11 @@ export function applyHighlightsToHTML(
|
|||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
appliedCount++
|
appliedCount++
|
||||||
console.log('✅ Highlight applied successfully')
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('❌ Could not find match for highlight:', searchText.substring(0, 50))
|
console.warn('❌ Could not find match for highlight:', searchText.substring(0, 50))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🎉 Applied', appliedCount, '/', highlights.length, 'highlights')
|
|
||||||
|
|
||||||
return tempDiv.innerHTML
|
return tempDiv.innerHTML
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export function applyTheme(
|
|||||||
|
|
||||||
// Listen for system theme changes
|
// Listen for system theme changes
|
||||||
mediaQueryListener = (e: MediaQueryListEvent) => {
|
mediaQueryListener = (e: MediaQueryListEvent) => {
|
||||||
console.log('🎨 System theme changed to:', e.matches ? 'dark' : 'light')
|
|
||||||
// The CSS media query handles the color changes automatically
|
// The CSS media query handles the color changes automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,5 +58,4 @@ export function applyTheme(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🎨 Applied theme:', theme, 'with colors:', { dark: darkColorTheme, light: lightColorTheme })
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,26 +11,21 @@ export function normalizeUrl(url: string): string {
|
|||||||
|
|
||||||
export function filterHighlightsByUrl(highlights: Highlight[], selectedUrl: string | undefined): Highlight[] {
|
export function filterHighlightsByUrl(highlights: Highlight[], selectedUrl: string | undefined): Highlight[] {
|
||||||
if (!selectedUrl || highlights.length === 0) {
|
if (!selectedUrl || highlights.length === 0) {
|
||||||
console.log('🔍 filterHighlightsByUrl: No URL or highlights', { selectedUrl, count: highlights.length })
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔍 filterHighlightsByUrl:', { selectedUrl, totalHighlights: highlights.length })
|
|
||||||
|
|
||||||
// For Nostr articles, we already fetched highlights specifically for this article
|
// For Nostr articles, we already fetched highlights specifically for this article
|
||||||
// So we don't need to filter them - they're all relevant
|
// So we don't need to filter them - they're all relevant
|
||||||
if (selectedUrl.startsWith('nostr:')) {
|
if (selectedUrl.startsWith('nostr:')) {
|
||||||
console.log('📌 Nostr article - returning all', highlights.length, 'highlights')
|
|
||||||
return highlights
|
return highlights
|
||||||
}
|
}
|
||||||
|
|
||||||
// For web URLs, filter by URL matching
|
// For web URLs, filter by URL matching
|
||||||
const normalizedSelected = normalizeUrl(selectedUrl)
|
const normalizedSelected = normalizeUrl(selectedUrl)
|
||||||
console.log('🔗 Normalized selected URL:', normalizedSelected)
|
|
||||||
|
|
||||||
const filtered = highlights.filter(h => {
|
const filtered = highlights.filter(h => {
|
||||||
if (!h.urlReference) {
|
if (!h.urlReference) {
|
||||||
console.log('⚠️ Highlight has no urlReference:', h.id, 'eventReference:', h.eventReference)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const normalizedRef = normalizeUrl(h.urlReference)
|
const normalizedRef = normalizeUrl(h.urlReference)
|
||||||
@@ -39,14 +34,11 @@ export function filterHighlightsByUrl(highlights: Highlight[], selectedUrl: stri
|
|||||||
normalizedRef.includes(normalizedSelected)
|
normalizedRef.includes(normalizedSelected)
|
||||||
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
console.log('✅ URL match:', normalizedRef)
|
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ URL mismatch:', normalizedRef, 'vs', normalizedSelected)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('📊 Filtered to', filtered.length, 'highlights')
|
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user