2025-02-14 13:15:10 +01:00
|
|
|
import { NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk'
|
|
|
|
import { FALLBACK_PROFILE_IMAGE } from '../../constants'
|
2025-02-19 19:19:42 +01:00
|
|
|
import { useAppSelector, useLocalCache } from 'hooks'
|
2025-02-14 13:15:10 +01:00
|
|
|
import { useProfile } from 'hooks/useProfile'
|
2025-02-21 13:13:33 +01:00
|
|
|
import {
|
|
|
|
Navigate,
|
|
|
|
useActionData,
|
|
|
|
useNavigation,
|
|
|
|
useSubmit
|
|
|
|
} from 'react-router-dom'
|
2025-02-14 13:15:10 +01:00
|
|
|
import { appRoutes } from 'routes'
|
2025-02-21 13:13:33 +01:00
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
2025-02-19 19:19:42 +01:00
|
|
|
import { adjustTextareaHeight, NOTE_DRAFT_CACHE_KEY } from 'utils'
|
2025-02-14 13:15:10 +01:00
|
|
|
import { NotePreview } from './NotePreview'
|
2025-02-21 13:13:33 +01:00
|
|
|
import { NoteSubmitActionResult, NoteSubmitForm } from 'types'
|
|
|
|
import { InputError } from 'components/Inputs/Error'
|
|
|
|
import { AlertPopup } from 'components/AlertPopup'
|
2025-02-14 13:15:10 +01:00
|
|
|
|
|
|
|
interface NoteSubmitProps {
|
|
|
|
initialContent?: string | undefined
|
2025-02-14 13:50:22 +01:00
|
|
|
handleClose?: () => void | undefined
|
2025-02-14 13:15:10 +01:00
|
|
|
}
|
|
|
|
|
2025-02-14 13:50:22 +01:00
|
|
|
export const NoteSubmit = ({
|
|
|
|
initialContent,
|
|
|
|
handleClose
|
|
|
|
}: NoteSubmitProps) => {
|
2025-02-14 17:29:06 +01:00
|
|
|
const navigation = useNavigation()
|
2025-02-14 13:15:10 +01:00
|
|
|
const userState = useAppSelector((state) => state.user)
|
|
|
|
const profile = useProfile(userState.user?.pubkey as string | undefined, {
|
|
|
|
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
|
|
|
})
|
2025-02-19 19:19:42 +01:00
|
|
|
const [cache, setCache] = useLocalCache<NoteSubmitForm>(NOTE_DRAFT_CACHE_KEY)
|
|
|
|
const [content, setContent] = useState(initialContent ?? cache?.content ?? '')
|
|
|
|
const [nsfw, setNsfw] = useState(cache?.nsfw ?? false)
|
2025-02-14 13:50:22 +01:00
|
|
|
const [showPreview, setShowPreview] = useState(!!initialContent)
|
2025-02-21 13:13:33 +01:00
|
|
|
const image = useMemo(
|
|
|
|
() => profile?.image || FALLBACK_PROFILE_IMAGE,
|
|
|
|
[profile?.image]
|
|
|
|
)
|
2025-02-14 13:50:22 +01:00
|
|
|
const ref = useRef<HTMLTextAreaElement>(null)
|
2025-02-14 13:15:10 +01:00
|
|
|
const submit = useSubmit()
|
2025-02-21 13:13:33 +01:00
|
|
|
const actionData = useActionData() as NoteSubmitActionResult
|
|
|
|
const formErrors = useMemo(
|
|
|
|
() =>
|
|
|
|
actionData?.type === 'validation' ? actionData.formErrors : undefined,
|
|
|
|
[actionData]
|
|
|
|
)
|
2025-02-14 13:50:22 +01:00
|
|
|
useEffect(() => {
|
2025-02-19 19:19:42 +01:00
|
|
|
if (ref.current && (!!initialContent || !!cache?.content)) {
|
2025-02-14 13:50:22 +01:00
|
|
|
adjustTextareaHeight(ref.current)
|
|
|
|
ref.current.focus()
|
|
|
|
}
|
2025-02-19 19:19:42 +01:00
|
|
|
}, [cache?.content, initialContent])
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setCache({
|
|
|
|
content,
|
|
|
|
nsfw
|
|
|
|
})
|
|
|
|
}, [content, nsfw, setCache])
|
2025-02-14 13:50:22 +01:00
|
|
|
|
2025-02-25 12:47:02 +01:00
|
|
|
useEffect(() => {
|
|
|
|
if (ref.current) adjustTextareaHeight(ref.current)
|
|
|
|
}, [content])
|
|
|
|
|
2025-02-21 13:13:33 +01:00
|
|
|
const [showTryAgainPopup, setShowTryAgainPopup] = useState<boolean>(false)
|
|
|
|
useEffect(() => {
|
|
|
|
const isTimeout = actionData?.type === 'timeout'
|
|
|
|
setShowTryAgainPopup(isTimeout)
|
|
|
|
if (isTimeout && actionData.action.intent === 'submit') {
|
|
|
|
setContent(actionData.action.data.content)
|
|
|
|
setNsfw(actionData.action.data.nsfw)
|
|
|
|
}
|
|
|
|
}, [actionData])
|
2025-02-14 13:15:10 +01:00
|
|
|
|
2025-02-21 13:13:33 +01:00
|
|
|
const handleFormSubmit = useCallback(
|
|
|
|
async (event?: React.FormEvent<HTMLFormElement>) => {
|
|
|
|
event?.preventDefault()
|
|
|
|
const formSubmit = {
|
|
|
|
intent: 'submit',
|
|
|
|
data: {
|
|
|
|
content,
|
|
|
|
nsfw
|
|
|
|
}
|
2025-02-19 21:22:56 +01:00
|
|
|
}
|
2025-02-14 17:39:49 +01:00
|
|
|
|
2025-02-21 13:13:33 +01:00
|
|
|
// Reset form
|
|
|
|
setContent('')
|
|
|
|
setNsfw(false)
|
2025-02-14 17:39:49 +01:00
|
|
|
|
2025-02-25 12:47:02 +01:00
|
|
|
setShowPreview(false)
|
|
|
|
|
2025-02-21 13:13:33 +01:00
|
|
|
submit(JSON.stringify(formSubmit), {
|
|
|
|
method: 'post',
|
|
|
|
encType: 'application/json',
|
|
|
|
action: appRoutes.feed
|
|
|
|
})
|
2025-02-14 17:40:49 +01:00
|
|
|
|
2025-02-21 13:13:33 +01:00
|
|
|
typeof handleClose === 'function' && handleClose()
|
|
|
|
},
|
|
|
|
[content, handleClose, nsfw, submit]
|
|
|
|
)
|
|
|
|
|
|
|
|
const handleTryAgainConfirm = useCallback(
|
|
|
|
(confirm: boolean) => {
|
|
|
|
setShowTryAgainPopup(false)
|
|
|
|
|
|
|
|
// Cancel if not confirmed
|
|
|
|
if (!confirm) return
|
|
|
|
|
|
|
|
handleFormSubmit()
|
|
|
|
},
|
|
|
|
[handleFormSubmit]
|
|
|
|
)
|
|
|
|
const handleContentChange = (
|
|
|
|
event: React.ChangeEvent<HTMLTextAreaElement>
|
|
|
|
) => {
|
|
|
|
setContent(event.currentTarget.value)
|
2025-02-14 13:15:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const handlePreviewToggle = () => {
|
|
|
|
setShowPreview((prev) => !prev)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!userState.user?.pubkey) return <Navigate to={appRoutes.home} />
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<form className='feedPostsPost' onSubmit={handleFormSubmit}>
|
|
|
|
<div className='feedPostsPostInside'>
|
|
|
|
<div className='feedPostsPostInsideInputWrapper'>
|
|
|
|
<div className='feedPostsPostInsidePP'>
|
|
|
|
<div className='IBMSMSMBSSCL_CommentTopPPWrapper'>
|
|
|
|
<div
|
|
|
|
className='IBMSMSMBSSCL_CommentTopPP'
|
|
|
|
style={{
|
|
|
|
background: `url(${image}) center/cover no-repeat`,
|
|
|
|
width: '50px',
|
|
|
|
height: '50px',
|
|
|
|
borderRadius: '8px'
|
|
|
|
}}
|
|
|
|
></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<textarea
|
|
|
|
id='postSocialTextarea'
|
|
|
|
name='content'
|
|
|
|
className='inputMain feedPostsPostInsideInput'
|
|
|
|
placeholder='Watcha thinking about?'
|
2025-02-14 13:50:22 +01:00
|
|
|
ref={ref}
|
2025-02-14 13:15:10 +01:00
|
|
|
value={content}
|
|
|
|
onChange={handleContentChange}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className='feedPostsPostInsideAction'>
|
|
|
|
<div
|
|
|
|
className='inputLabelWrapperMain inputLabelWrapperMainAlt'
|
|
|
|
style={{ width: 'unset' }}
|
|
|
|
>
|
|
|
|
<input
|
|
|
|
type='checkbox'
|
|
|
|
className='CheckboxMain'
|
|
|
|
checked={nsfw}
|
|
|
|
id='nsfw'
|
|
|
|
name='nsfw'
|
|
|
|
onChange={() => setNsfw((nsfw) => !nsfw)}
|
|
|
|
/>
|
|
|
|
<label htmlFor='nsfw' className='form-label labelMain'>
|
|
|
|
NSFW
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
style={{ display: 'flex', flexDirection: 'row', gridGap: '10px' }}
|
|
|
|
>
|
2025-02-14 13:50:22 +01:00
|
|
|
{typeof handleClose === 'function' && (
|
|
|
|
<button
|
|
|
|
className='btn btnMain'
|
|
|
|
type='button'
|
|
|
|
style={{ padding: '5px 20px', borderRadius: '8px' }}
|
|
|
|
onClick={handleClose}
|
|
|
|
>
|
|
|
|
Close
|
|
|
|
</button>
|
|
|
|
)}
|
2025-02-14 13:15:10 +01:00
|
|
|
<button
|
|
|
|
className='btn btnMain'
|
|
|
|
type='button'
|
|
|
|
style={{ padding: '5px 20px', borderRadius: '8px' }}
|
|
|
|
onClick={handlePreviewToggle}
|
|
|
|
>
|
|
|
|
Preview
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
className='btn btnMain'
|
|
|
|
type='submit'
|
|
|
|
style={{ padding: '5px 20px', borderRadius: '8px' }}
|
2025-02-21 13:13:33 +01:00
|
|
|
disabled={navigation.state !== 'idle' || !content.length}
|
2025-02-14 13:15:10 +01:00
|
|
|
>
|
2025-02-20 20:50:21 +01:00
|
|
|
{navigation.state === 'submitting' ? 'Posting...' : 'Post'}
|
2025-02-14 13:15:10 +01:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
2025-02-21 13:13:33 +01:00
|
|
|
{typeof formErrors?.content !== 'undefined' && (
|
|
|
|
<InputError message={formErrors?.content} />
|
|
|
|
)}
|
2025-02-14 22:48:58 +00:00
|
|
|
{showPreview && <NotePreview content={content} />}
|
2025-02-21 13:13:33 +01:00
|
|
|
{showTryAgainPopup && (
|
|
|
|
<AlertPopup
|
|
|
|
handleConfirm={handleTryAgainConfirm}
|
|
|
|
handleClose={() => setShowTryAgainPopup(false)}
|
|
|
|
header={'Post'}
|
|
|
|
label={`Posting timed out. Do you want to try again?`}
|
|
|
|
/>
|
|
|
|
)}
|
2025-02-14 13:15:10 +01:00
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|