Browse Source

fix quotes

imwald
Silberengel 2 weeks ago
parent
commit
2333b054d6
  1. 12
      src/components/PostEditor/PostContent.tsx
  2. 12
      src/components/PostEditor/PostTextarea/index.tsx
  3. 2
      src/components/PostEditor/index.tsx
  4. 23
      src/services/post-editor-cache.service.test.ts
  5. 22
      src/services/post-editor-cache.service.ts

12
src/components/PostEditor/PostContent.tsx

@ -156,6 +156,7 @@ function formatMarkupImageAtCursor(url: string, asciidoc: boolean): string { @@ -156,6 +156,7 @@ function formatMarkupImageAtCursor(url: string, asciidoc: boolean): string {
}
export default function PostContent({
open,
defaultContent = '',
parentEvent,
close,
@ -165,6 +166,8 @@ export default function PostContent({ @@ -165,6 +166,8 @@ export default function PostContent({
onPublishSuccess,
discussionDynamicTopics
}: {
/** When false, the post shell is closed (e.g. dialog). Used to re-sync the TipTap body when reopened. */
open: boolean
defaultContent?: string
parentEvent?: Event
close: () => void
@ -717,6 +720,15 @@ export default function PostContent({ @@ -717,6 +720,15 @@ export default function PostContent({
)
}, [getDeterminedKind, defaultContent, parentEvent, isNsfw, isPoll, pollCreateData, addClientTag])
const prevComposerShellOpenRef = useRef(open)
useEffect(() => {
const wasOpen = prevComposerShellOpenRef.current
prevComposerShellOpenRef.current = open
if (!wasOpen && open && !advancedLabOpenRef.current) {
textareaRef.current?.syncFromPostCache()
}
}, [open, getDeterminedKind, defaultContent, parentEvent])
const rssReplyExtraPreviewTags = useMemo((): string[][] | undefined => {
if (!parentEvent || parentEvent.kind !== ExtendedKind.RSS_THREAD_ROOT) return undefined
const raw =

12
src/components/PostEditor/PostTextarea/index.tsx

@ -43,6 +43,8 @@ export type TPostTextareaHandle = { @@ -43,6 +43,8 @@ export type TPostTextareaHandle = {
insertText: (text: string) => void
insertEmoji: (emoji: string | TEmoji) => void
clear: () => void
/** Re-read `postEditorCache` / `defaultContent` into TipTap (dialog reopened; initial `content` only runs once). */
syncFromPostCache: () => void
getText: () => string
/** Replace editor from plain `content` (e.g. advanced lab). Syncs TipTap JSON cache and parent `text`. */
setDocumentFromPlainText: (plain: string) => void
@ -308,6 +310,16 @@ const PostTextarea = forwardRef< @@ -308,6 +310,16 @@ const PostTextarea = forwardRef<
setText('')
}
},
syncFromPostCache: () => {
const editor = editorRef.current
if (!editor) return
const next = postEditorCache.getPostContentCache({ kind, defaultContent, parentEvent })
if (next === undefined) return
editor.chain().setContent(next).run()
const json = editor.getJSON()
setText(parseEditorJsonToText(json))
postEditorCache.setPostContentCache({ kind, defaultContent, parentEvent }, json)
},
getText: () => {
const editor = editorRef.current
if (editor) {

2
src/components/PostEditor/index.tsx

@ -60,6 +60,7 @@ export default function PostEditor({ @@ -60,6 +60,7 @@ export default function PostEditor({
const content = useMemo(() => {
return (
<PostContent
open={open}
defaultContent={effectiveDefaultContent}
parentEvent={parentEvent}
close={() => setOpen(false)}
@ -71,6 +72,7 @@ export default function PostEditor({ @@ -71,6 +72,7 @@ export default function PostEditor({
/>
)
}, [
open,
effectiveDefaultContent,
parentEvent,
openFrom,

23
src/services/post-editor-cache.service.test.ts

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
import { afterEach, describe, expect, it } from 'vitest'
import postEditorCache from '@/services/post-editor-cache.service'
import { plainTextToTipTapDoc } from '@/lib/tiptap'
describe('PostEditorCacheService — quote / defaultContent', () => {
afterEach(() => {
postEditorCache.clearAllPostCaches()
})
it('getPostContentCache ignores an empty cached doc when defaultContent is set (re-seed quote)', () => {
const params = { kind: 1 as const, defaultContent: '\nnostr:nevent1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' }
postEditorCache.setPostContentCache(params, plainTextToTipTapDoc(''))
const got = postEditorCache.getPostContentCache(params)
expect(typeof got).toBe('string')
expect(got).toContain('nostr:')
})
it('setPostContentCache does not persist an empty body when defaultContent is set', () => {
const params = { kind: 1 as const, defaultContent: '\nnostr:nevent1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' }
postEditorCache.setPostContentCache(params, plainTextToTipTapDoc(''))
expect(postEditorCache.getPostContentCache(params)).toContain('nostr:')
})
})

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

@ -184,7 +184,20 @@ class PostEditorCacheService { @@ -184,7 +184,20 @@ class PostEditorCacheService {
this.restoreFromStorageIfNeeded()
const cacheKey = this.generateCacheKey({ kind, defaultContent, parentEvent })
const cached = this.postContentCache.get(cacheKey)
if (cached !== undefined) return cached
if (cached !== undefined) {
const cachedText = (
typeof cached === 'string' ? cached : parseEditorJsonToText(cached ?? undefined)
).trim()
// Seeded composers (e.g. Quote): an empty cached doc must not hide `defaultContent` on reopen.
if (
cachedText === '' &&
defaultContent !== undefined &&
defaultContent.trim() !== ''
) {
return this.escapeAmpersandsForHtml(defaultContent)
}
return cached
}
if (defaultContent !== undefined && defaultContent !== '') {
return this.escapeAmpersandsForHtml(defaultContent)
}
@ -208,6 +221,13 @@ class PostEditorCacheService { @@ -208,6 +221,13 @@ class PostEditorCacheService {
) {
return
}
if (incomingText === '' && defaultContent !== undefined && defaultContent.trim() !== '') {
this.keysRestoredThisSession.delete(cacheKey)
if (this.postContentCache.delete(cacheKey)) {
this.schedulePersist()
}
return
}
this.keysRestoredThisSession.delete(cacheKey)
this.postContentCache.set(cacheKey, content)
this.schedulePersist()

Loading…
Cancel
Save