Browse Source

handle freezing

imwald
Silberengel 2 weeks ago
parent
commit
e5b36499e9
  1. 7
      src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx
  2. 15
      src/components/PostEditor/PostContent.tsx
  3. 17
      src/components/PostEditor/PostRelaySelector.tsx
  4. 20
      src/services/client.service.ts
  5. 27
      src/services/post-editor-cache.service.ts

7
src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx

@ -297,7 +297,8 @@ export default function AdvancedEventLabDialog({ @@ -297,7 +297,8 @@ export default function AdvancedEventLabDialog({
setUndoUiTick((n) => n + 1)
}, [])
const flushLabDraftNow = useCallback((key: string) => {
/** Writes the live CodeMirror doc into the draft cache. `urgent` flushes localStorage synchronously (tab hide / unload only). */
const flushLabDraftNow = useCallback((key: string, urgent = false) => {
const v = markupView.current
const s = sliceRef.current
if (!v || !s) return
@ -310,14 +311,14 @@ export default function AdvancedEventLabDialog({ @@ -310,14 +311,14 @@ export default function AdvancedEventLabDialog({
content: v.state.doc.toString(),
tags: s.tags.map((row) => [...row])
})
postEditorCache.flushPersist()
if (urgent) postEditorCache.flushPersist()
}, [])
useEffect(() => {
if (!open || !draftPersistenceKey) return
const key = draftPersistenceKey
const onPageLeave = () => {
flushLabDraftNow(key)
flushLabDraftNow(key, true)
}
window.addEventListener('pagehide', onPageLeave)
window.addEventListener('beforeunload', onPageLeave)

15
src/components/PostEditor/PostContent.tsx

@ -120,6 +120,14 @@ import { @@ -120,6 +120,14 @@ import {
import { parseLabSlice, type AdvancedEventLabSlice } from '@/lib/advanced-event-lab-slice'
import { isAsciidocMarkupKind } from '@/lib/advanced-event-lab-kinds'
/** Let the UI paint before heavy work. `requestAnimationFrame` alone can stall indefinitely in hidden or throttled documents. */
function yieldForPaintBeforeHeavyWork(): Promise<void> {
return Promise.race([
new Promise<void>((resolve) => requestAnimationFrame(() => resolve())),
new Promise<void>((resolve) => setTimeout(resolve, 50))
])
}
function stripUrlForImageExtensionCheck(url: string): string {
return url.trim().split(/[#?]/)[0].toLowerCase()
}
@ -1217,6 +1225,8 @@ export default function PostContent({ @@ -1217,6 +1225,8 @@ export default function PostContent({
return
}
try {
// Let the browser paint any loading/disabled UI before draft build + CodeMirror mount (can be heavy).
await yieldForPaintBeforeHeavyWork()
const body = textareaRef.current?.getText() ?? text
const cleanedText = rewritePlainTextHttpUrls(body)
let d = await createDraftEvent(cleanedText)
@ -1307,7 +1317,10 @@ export default function PostContent({ @@ -1307,7 +1317,10 @@ export default function PostContent({
setPosting(true)
let newEvent: any = null
let draftEvent: any = null
// Allow "Publishing…" (and other posting UI) to paint before draft build + network work.
await yieldForPaintBeforeHeavyWork()
try {
// Clean tracking parameters from URLs in the post content
const cleanedText = rewritePlainTextHttpUrls(text)

17
src/components/PostEditor/PostRelaySelector.tsx

@ -19,6 +19,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover @@ -19,6 +19,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
import logger from '@/lib/logger'
import { computePrePublishRelayCapPreview, type TPrePublishRelayCapPreview } from '@/lib/pre-publish-relay-cap'
import client from '@/services/client.service'
/** Stable default when `mentions` is omitted — inline `= []` is a new array every render and retriggers effects. */
const NO_MENTIONS: string[] = []
@ -265,6 +266,22 @@ export default function PostRelaySelector({ @@ -265,6 +266,22 @@ export default function PostRelaySelector({
}
}, [selectedRelayUrls, hasManualSelection, isLoading, describeRelaySelection])
/** Picker lists exclude global read-only relays; session-strike skips are cleared when a relay is newly chosen so publishes honor the list. */
const prevSelectedNormalizedRef = useRef<Set<string>>(new Set())
useEffect(() => {
const norm = (u: string) => normalizeAnyRelayUrl(u) || u
const prev = prevSelectedNormalizedRef.current
const newlyAdded: string[] = []
for (const url of selectedRelayUrls) {
const n = norm(url)
if (!prev.has(n)) newlyAdded.push(url)
}
prevSelectedNormalizedRef.current = new Set(selectedRelayUrls.map(norm))
if (newlyAdded.length > 0) {
client.clearSessionRelayStrikesForUrls(newlyAdded)
}
}, [selectedRelayUrls])
// Update parent component with selected relays
useEffect(() => {
// An event is "protected" if we have selected relays that aren't the default user write relays

20
src/services/client.service.ts

@ -1164,6 +1164,26 @@ class ClientService extends EventTarget { @@ -1164,6 +1164,26 @@ class ClientService extends EventTarget {
return had
}
/**
* Clear session strikes for several URLs at once (e.g. publish relay picker). One UI notification.
*/
clearSessionRelayStrikesForUrls(urls: string[]): number {
let cleared = 0
for (const url of urls) {
const n = normalizeAnyRelayUrl(url) || url
if (!n) continue
if (this.publishStrikeCount.delete(n)) cleared += 1
}
if (cleared > 0) {
logger.info('[Relay] Session strikes cleared for relays (added to publish selection)', {
cleared,
urlCount: urls.length
})
this.notifySessionRelayStrikesChanged()
}
return cleared
}
/**
* Apply strike filter; if that removes all candidates while some were provided, clear strikes **for those URLs
* only** and retry once. (A global clear here caused storms: e.g. NIP-65 outbox retry with 2 relays wiped strikes

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

@ -263,11 +263,10 @@ class PostEditorCacheService { @@ -263,11 +263,10 @@ class PostEditorCacheService {
clearAdvancedLabDraft(key: string) {
this.restoreFromStorageIfNeeded()
if (!this.advancedLabDrafts.delete(key)) return
if (this.persistTimeoutId) {
clearTimeout(this.persistTimeoutId)
this.persistTimeoutId = null
}
this.persistNow()
// Avoid synchronous JSON.stringify(localStorage) of the full draft blob here — that blocks
// the main thread when TipTap caches are large. Debounced persist is enough; tab close still
// uses {@link flushPersist} via beforeunload.
this.schedulePersist()
}
clearPostCache({ kind, defaultContent, parentEvent }: TCacheKeyParams) {
@ -276,11 +275,7 @@ class PostEditorCacheService { @@ -276,11 +275,7 @@ class PostEditorCacheService {
this.postContentCache.delete(cacheKey)
this.postSettingsCache.delete(cacheKey)
this.advancedLabDrafts.delete(cacheKey)
if (this.persistTimeoutId) {
clearTimeout(this.persistTimeoutId)
this.persistTimeoutId = null
}
this.persistNow()
this.schedulePersist()
}
/** Clear all post and settings drafts. Use when user explicitly clears caches. */
@ -289,11 +284,7 @@ class PostEditorCacheService { @@ -289,11 +284,7 @@ class PostEditorCacheService {
this.postContentCache.clear()
this.postSettingsCache.clear()
this.advancedLabDrafts.clear()
if (this.persistTimeoutId) {
clearTimeout(this.persistTimeoutId)
this.persistTimeoutId = null
}
this.persistNow()
this.schedulePersist()
}
generateCacheKey({ kind, parentEvent }: TCacheKeyParams): string {
@ -315,11 +306,7 @@ class PostEditorCacheService { @@ -315,11 +306,7 @@ class PostEditorCacheService {
clearThreadDraft(): void {
this.threadDraftCache = null
if (this.persistTimeoutId) {
clearTimeout(this.persistTimeoutId)
this.persistTimeoutId = null
}
this.persistNow()
this.schedulePersist()
}
}

Loading…
Cancel
Save