feat: cache post content
This commit is contained in:
@@ -4,9 +4,10 @@ import { createCommentDraftEvent, createShortTextNoteDraftEvent } from '@/lib/dr
|
|||||||
import { getRootEventTag } from '@/lib/event.ts'
|
import { getRootEventTag } from '@/lib/event.ts'
|
||||||
import { useNostr } from '@/providers/NostrProvider'
|
import { useNostr } from '@/providers/NostrProvider'
|
||||||
import client from '@/services/client.service'
|
import client from '@/services/client.service'
|
||||||
|
import postContentCache from '@/services/post-content-cache.service'
|
||||||
import { ChevronDown, ImageUp, LoaderCircle } from 'lucide-react'
|
import { ChevronDown, ImageUp, LoaderCircle } from 'lucide-react'
|
||||||
import { Event, kinds, nip19 } from 'nostr-tools'
|
import { Event, kinds, nip19 } from 'nostr-tools'
|
||||||
import { useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import TextareaWithMentions from '../TextareaWithMentions.tsx'
|
import TextareaWithMentions from '../TextareaWithMentions.tsx'
|
||||||
import Mentions from './Mentions'
|
import Mentions from './Mentions'
|
||||||
@@ -27,7 +28,7 @@ export default function NormalPostContent({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
const { publish, checkLogin } = useNostr()
|
const { publish, checkLogin } = useNostr()
|
||||||
const [content, setContent] = useState(defaultContent)
|
const [content, setContent] = useState('')
|
||||||
const [pictureInfos, setPictureInfos] = useState<{ url: string; tags: string[][] }[]>([])
|
const [pictureInfos, setPictureInfos] = useState<{ url: string; tags: string[][] }[]>([])
|
||||||
const [posting, setPosting] = useState(false)
|
const [posting, setPosting] = useState(false)
|
||||||
const [showMoreOptions, setShowMoreOptions] = useState(false)
|
const [showMoreOptions, setShowMoreOptions] = useState(false)
|
||||||
@@ -35,8 +36,26 @@ export default function NormalPostContent({
|
|||||||
const [specifiedRelayUrls, setSpecifiedRelayUrls] = useState<string[] | undefined>(undefined)
|
const [specifiedRelayUrls, setSpecifiedRelayUrls] = useState<string[] | undefined>(undefined)
|
||||||
const [uploadingPicture, setUploadingPicture] = useState(false)
|
const [uploadingPicture, setUploadingPicture] = useState(false)
|
||||||
const [mentions, setMentions] = useState<string[]>([])
|
const [mentions, setMentions] = useState<string[]>([])
|
||||||
|
const [cursorOffset, setCursorOffset] = useState(0)
|
||||||
|
const initializedRef = useRef(false)
|
||||||
const canPost = !!content && !posting
|
const canPost = !!content && !posting
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cachedContent = postContentCache.get({ defaultContent, parentEvent })
|
||||||
|
if (cachedContent) {
|
||||||
|
setContent(cachedContent)
|
||||||
|
}
|
||||||
|
if (defaultContent) {
|
||||||
|
setCursorOffset(defaultContent.length)
|
||||||
|
}
|
||||||
|
initializedRef.current = true
|
||||||
|
}, [defaultContent, parentEvent])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!initializedRef.current) return
|
||||||
|
postContentCache.set({ defaultContent, parentEvent }, content)
|
||||||
|
}, [content])
|
||||||
|
|
||||||
const post = async (e: React.MouseEvent) => {
|
const post = async (e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
checkLogin(async () => {
|
checkLogin(async () => {
|
||||||
@@ -130,6 +149,7 @@ export default function NormalPostContent({
|
|||||||
setTextValue={setContent}
|
setTextValue={setContent}
|
||||||
textValue={content}
|
textValue={content}
|
||||||
placeholder={t('Write something...')}
|
placeholder={t('Write something...')}
|
||||||
|
cursorOffset={cursorOffset}
|
||||||
/>
|
/>
|
||||||
{content && <Preview content={content} />}
|
{content && <Preview content={content} />}
|
||||||
<SendOnlyToSwitch
|
<SendOnlyToSwitch
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|||||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||||
import { Event } from 'nostr-tools'
|
import { Event } from 'nostr-tools'
|
||||||
import { Dispatch, useMemo } from 'react'
|
import { Dispatch, useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet'
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet'
|
||||||
import NormalPostContent from './NormalPostContent'
|
import NormalPostContent from './NormalPostContent'
|
||||||
import PicturePostContent from './PicturePostContent'
|
import PicturePostContent from './PicturePostContent'
|
||||||
import Title from './Title'
|
import Title from './Title'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function PostEditor({
|
export default function PostEditor({
|
||||||
defaultContent = '',
|
defaultContent = '',
|
||||||
@@ -55,7 +55,7 @@ export default function PostEditor({
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
)
|
)
|
||||||
}, [parentEvent])
|
}, [parentEvent, defaultContent])
|
||||||
|
|
||||||
if (isSmallScreen) {
|
if (isSmallScreen) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ import { getCurrentWord, replaceWord } from './utils'
|
|||||||
export default function TextareaWithMentions({
|
export default function TextareaWithMentions({
|
||||||
textValue,
|
textValue,
|
||||||
setTextValue,
|
setTextValue,
|
||||||
|
cursorOffset = 0,
|
||||||
...props
|
...props
|
||||||
}: ComponentProps<'textarea'> & {
|
}: ComponentProps<'textarea'> & {
|
||||||
textValue: string
|
textValue: string
|
||||||
setTextValue: Dispatch<SetStateAction<string>>
|
setTextValue: Dispatch<SetStateAction<string>>
|
||||||
|
cursorOffset?: number
|
||||||
}) {
|
}) {
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||||
@@ -33,6 +35,15 @@ export default function TextareaWithMentions({
|
|||||||
const [debouncedCommandValue, setDebouncedCommandValue] = useState(commandValue)
|
const [debouncedCommandValue, setDebouncedCommandValue] = useState(commandValue)
|
||||||
const [profiles, setProfiles] = useState<TProfile[]>([])
|
const [profiles, setProfiles] = useState<TProfile[]>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (textareaRef.current && cursorOffset !== 0) {
|
||||||
|
const textarea = textareaRef.current
|
||||||
|
const newPos = Math.max(0, textarea.selectionStart - cursorOffset)
|
||||||
|
textarea.setSelectionRange(newPos, newPos)
|
||||||
|
textarea.focus()
|
||||||
|
}
|
||||||
|
}, [cursorOffset])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handler = setTimeout(() => {
|
const handler = setTimeout(() => {
|
||||||
setDebouncedCommandValue(commandValue)
|
setDebouncedCommandValue(commandValue)
|
||||||
|
|||||||
32
src/services/post-content-cache.service.ts
Normal file
32
src/services/post-content-cache.service.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { Event } from 'nostr-tools'
|
||||||
|
|
||||||
|
class PostContentCacheService {
|
||||||
|
static instance: PostContentCacheService
|
||||||
|
|
||||||
|
private cache: Map<string, string> = new Map()
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (!PostContentCacheService.instance) {
|
||||||
|
PostContentCacheService.instance = this
|
||||||
|
}
|
||||||
|
return PostContentCacheService.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
get({ defaultContent, parentEvent }: { defaultContent?: string; parentEvent?: Event } = {}) {
|
||||||
|
return this.cache.get(this.generateCacheKey(defaultContent, parentEvent)) ?? defaultContent
|
||||||
|
}
|
||||||
|
|
||||||
|
set(
|
||||||
|
{ defaultContent, parentEvent }: { defaultContent?: string; parentEvent?: Event },
|
||||||
|
content: string
|
||||||
|
) {
|
||||||
|
this.cache.set(this.generateCacheKey(defaultContent, parentEvent), content)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCacheKey(defaultContent: string = '', parentEvent?: Event): string {
|
||||||
|
return parentEvent ? parentEvent.id : defaultContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = new PostContentCacheService()
|
||||||
|
export default instance
|
||||||
Reference in New Issue
Block a user