Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
82181ffb43
  1. 17
      src/hooks/useQuoteEvents.tsx
  2. 4
      src/providers/NostrProvider/index.tsx
  3. 57
      src/services/post-editor-cache.service.ts

17
src/hooks/useQuoteEvents.tsx

@ -35,11 +35,12 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) { @@ -35,11 +35,12 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) {
return
}
const ev = event
let cancelled = false
let loadTimeoutId: ReturnType<typeof setTimeout> | undefined
async function init() {
const noteRowId = event.id
const noteRowId = ev.id
const isNewTarget = lastSubscribedEventIdRef.current !== noteRowId
lastSubscribedEventIdRef.current = noteRowId
@ -60,7 +61,7 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) { @@ -60,7 +61,7 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) {
const userRelays = userRelayList?.read || []
const fromFeed = browsingRelayUrls.map((u) => normalizeUrl(u) || u).filter(Boolean)
const seenOn = client.getSeenEventRelayUrls(event.id)
const seenOn = client.getSeenEventRelayUrls(ev.id)
const eTagBlockedSet = new Set(
E_TAG_FILTER_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)
)
@ -76,12 +77,12 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) { @@ -76,12 +77,12 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) {
.filter(Boolean)
.filter((u) => !eTagBlockedSet.has(normalizeUrl(u) || u))
const filterQeId = isReplaceableEvent(event.kind)
? getReplaceableCoordinateFromEvent(event)
: event.id
const eventCoordinate = isReplaceableEvent(event.kind)
? getReplaceableCoordinateFromEvent(event)
: `${event.kind}:${event.pubkey}:${event.id}`
const filterQeId = isReplaceableEvent(ev.kind)
? getReplaceableCoordinateFromEvent(ev)
: ev.id
const eventCoordinate = isReplaceableEvent(ev.kind)
? getReplaceableCoordinateFromEvent(ev)
: `${ev.kind}:${ev.pubkey}:${ev.id}`
const { closer, timelineKey } = await client.subscribeTimeline(
[

4
src/providers/NostrProvider/index.tsx

@ -695,7 +695,9 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -695,7 +695,9 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
const prev = prevAccountPubkeyRef.current
const curr = account?.pubkey ?? null
prevAccountPubkeyRef.current = curr
if (prev !== undefined && prev !== curr) {
if (prev != null && curr != null && prev !== curr) {
postEditorCache.clearOnAccountChange()
} else if (prev != null && curr === null) {
postEditorCache.clearOnAccountChange()
}
}, [account?.pubkey])

57
src/services/post-editor-cache.service.ts

@ -3,8 +3,9 @@ import storage from '@/services/local-storage.service' @@ -3,8 +3,9 @@ import storage from '@/services/local-storage.service'
import { TPollCreateData } from '@/types'
import { Content } from '@tiptap/react'
import { Event } from 'nostr-tools'
import { parseEditorJsonToText } from '@/lib/tiptap'
const PERSIST_DEBOUNCE_MS = 30_000
const PERSIST_DEBOUNCE_MS = 5_000
type TPostSettings = {
isNsfw?: boolean
@ -41,14 +42,27 @@ class PostEditorCacheService { @@ -41,14 +42,27 @@ class PostEditorCacheService {
private threadDraftCache: TThreadDraft | null = null
private persistTimeoutId: ReturnType<typeof setTimeout> | null = null
private restoredFromStorage = false
private keysRestoredThisSession = new Set<string>()
constructor() {
if (!PostEditorCacheService.instance) {
PostEditorCacheService.instance = this
if (typeof window !== 'undefined') {
window.addEventListener('beforeunload', () => this.flushPersist())
}
}
return PostEditorCacheService.instance
}
/** Flush pending draft to localStorage immediately. Called on beforeunload so drafts survive reload. */
flushPersist() {
if (this.persistTimeoutId) {
clearTimeout(this.persistTimeoutId)
this.persistTimeoutId = null
}
this.persistNow()
}
/**
* Escape ampersands so that when TipTap parses initial content as HTML,
* sequences like &notify in URLs are not interpreted as the &not; entity (¬).
@ -57,6 +71,14 @@ class PostEditorCacheService { @@ -57,6 +71,14 @@ class PostEditorCacheService {
return text.replace(/&/g, '&amp;')
}
/** Normalize cache key so hex event ids are lowercase; ensures consistent lookup across sessions. */
private normalizeCacheKey(key: string): string {
const [, parentPart] = key.split(':', 2)
if (!parentPart) return key
const normalized = /^[0-9a-f]{64}$/i.test(parentPart) ? parentPart.toLowerCase() : parentPart
return `${key.split(':')[0]}:${normalized}`
}
private restoreFromStorageIfNeeded() {
if (this.restoredFromStorage) return
this.restoredFromStorage = true
@ -69,12 +91,16 @@ class PostEditorCacheService { @@ -69,12 +91,16 @@ class PostEditorCacheService {
if (data.accountPubkey !== account.pubkey) return
if (data.postContentCache && typeof data.postContentCache === 'object') {
Object.entries(data.postContentCache).forEach(([k, v]) => {
if (v) this.postContentCache.set(k, v)
if (v) {
const key = this.normalizeCacheKey(k)
this.postContentCache.set(key, v)
this.keysRestoredThisSession.add(key)
}
})
}
if (data.postSettingsCache && typeof data.postSettingsCache === 'object') {
Object.entries(data.postSettingsCache).forEach(([k, v]) => {
if (v) this.postSettingsCache.set(k, v)
if (v) this.postSettingsCache.set(this.normalizeCacheKey(k), v)
})
}
if (data.threadDraft) {
@ -128,6 +154,7 @@ class PostEditorCacheService { @@ -128,6 +154,7 @@ class PostEditorCacheService {
this.postContentCache.clear()
this.postSettingsCache.clear()
this.threadDraftCache = null
this.keysRestoredThisSession.clear()
this.restoredFromStorage = false
try {
window.localStorage.removeItem(StorageKey.POST_EDITOR_DRAFT)
@ -148,7 +175,23 @@ class PostEditorCacheService { @@ -148,7 +175,23 @@ class PostEditorCacheService {
}
setPostContentCache({ kind, defaultContent, parentEvent }: TCacheKeyParams, content: Content) {
this.restoreFromStorageIfNeeded()
const cacheKey = this.generateCacheKey({ kind, defaultContent, parentEvent })
const incomingText = (
typeof content === 'string' ? content : parseEditorJsonToText(content ?? undefined)
).trim()
const existing = this.postContentCache.get(cacheKey)
const existingText = existing
? (typeof existing === 'string' ? existing : parseEditorJsonToText(existing)).trim()
: ''
if (
incomingText === '' &&
existingText !== '' &&
this.keysRestoredThisSession.has(cacheKey)
) {
return
}
this.keysRestoredThisSession.delete(cacheKey)
this.postContentCache.set(cacheKey, content)
this.schedulePersist()
}
@ -166,6 +209,7 @@ class PostEditorCacheService { @@ -166,6 +209,7 @@ class PostEditorCacheService {
clearPostCache({ kind, defaultContent, parentEvent }: TCacheKeyParams) {
const cacheKey = this.generateCacheKey({ kind, defaultContent, parentEvent })
this.keysRestoredThisSession.delete(cacheKey)
this.postContentCache.delete(cacheKey)
this.postSettingsCache.delete(cacheKey)
if (this.persistTimeoutId) {
@ -177,6 +221,7 @@ class PostEditorCacheService { @@ -177,6 +221,7 @@ class PostEditorCacheService {
/** Clear all post and settings drafts. Use when user explicitly clears caches. */
clearAllPostCaches() {
this.keysRestoredThisSession.clear()
this.postContentCache.clear()
this.postSettingsCache.clear()
if (this.persistTimeoutId) {
@ -186,8 +231,10 @@ class PostEditorCacheService { @@ -186,8 +231,10 @@ class PostEditorCacheService {
this.persistNow()
}
generateCacheKey({ kind, defaultContent = '', parentEvent }: TCacheKeyParams): string {
const parentPart = parentEvent ? parentEvent.id : ''
generateCacheKey({ kind, parentEvent }: TCacheKeyParams): string {
if (!parentEvent?.id) return `${kind}:`
const id = parentEvent.id.trim()
const parentPart = /^[0-9a-f]{64}$/i.test(id) ? id.toLowerCase() : id
return `${kind}:${parentPart}`
}

Loading…
Cancel
Save