import { useCallback, useEffect, useRef, useState } from 'react'; import { useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import Draggable from 'react-draggable'; import { setAccount, useAccount, useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import { formatMarkdown } from '../../lib/utils/post-utils'; import { getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { isValidURL } from '../../lib/utils/url-utils'; import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils'; import useSelectedTextStore from '../../stores/use-selected-text-store'; import usePublishReply from '../../hooks/use-publish-reply'; import useIsMobile from '../../hooks/use-is-mobile'; import styles from './reply-modal.module.css'; import { LinkTypePreviewer } from '../post-form'; import _ from 'lodash'; import useAnonMode from '../../hooks/use-anon-mode'; interface ReplyModalProps { closeModal: () => void; showReplyModal: boolean; parentCid: string; postCid: string; scrollY: number; subplebbitAddress: string; } const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, subplebbitAddress }: ReplyModalProps) => { const { t } = useTranslation(); const { setPublishReplyOptions, publishReply, resetPublishReplyOptions, replyIndex } = usePublishReply({ cid: parentCid, subplebbitAddress }); const account = useAccount(); const { displayName } = account?.author || {}; const [url, setUrl] = useState(''); const textRef = useRef(null); const urlRef = useRef(null); const { selectedText } = useSelectedTextStore(); const { anonMode, getNewSigner, getExistingSigner } = useAnonMode(postCid); const comment = useComment({ commentCid: postCid }); const address = comment?.author?.address; const hasCalledAnonAddressRef = useRef(false); const getAnonAddressForReply = useCallback(async () => { if (anonMode && !hasCalledAnonAddressRef.current) { hasCalledAnonAddressRef.current = true; const existingSigner = await getExistingSigner(address); if (existingSigner) { setPublishReplyOptions({ signer: existingSigner, author: { address: existingSigner.address, displayName: displayName || undefined, }, }); } else { const newSigner = await getNewSigner(); if (newSigner) { setPublishReplyOptions({ signer: newSigner, author: { address: newSigner.address, displayName: displayName || undefined, }, }); } } } }, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName]); const onPublishReply = () => { const currentContent = textRef.current?.value.slice(contentPrefix.length).trim() || ''; const currentUrl = urlRef.current?.value.trim() || ''; if (!currentContent && !currentUrl) { alert(t('empty_comment_alert')); return; } if (currentUrl && !isValidURL(currentUrl)) { alert(t('invalid_url_alert')); return; } publishReply(); }; useEffect(() => { if (anonMode) { getAnonAddressForReply(); } }, [anonMode, getAnonAddressForReply]); useEffect(() => { if (typeof replyIndex === 'number') { resetPublishReplyOptions(); closeModal(); } }, [replyIndex, resetPublishReplyOptions, closeModal]); const nodeRef = useRef(null); const isMobile = useIsMobile(); useEffect(() => { if (nodeRef.current && isMobile) { const viewportHeight = window.innerHeight; const modalHeight = 150; const centeredPosition = scrollY + viewportHeight / 2 - modalHeight / 2; nodeRef.current.style.top = `${centeredPosition}px`; } }, [isMobile, scrollY]); const parentCidRef = useRef(null); useEffect(() => { if (parentCidRef.current && parentCidRef.current) { const cidWidth = parentCidRef.current.offsetWidth; parentCidRef.current.style.width = `${cidWidth}px`; } }, [parentCid]); const location = useLocation(); const isInAllView = isAllView(location.pathname, useParams()); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const subplebbit = useSubplebbit({ subplebbitAddress }); const { updatedAt } = subplebbit || {}; const isBoardOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60; const offlineAlert = updatedAt ? isBoardOffline && (
{`Posts last synced ${getFormattedTimeAgo(updatedAt)}, the subplebbit might be offline and publishing might fail.`}
) : `The subplebbit might be offline and publishing might fail.`; useEffect(() => { if (showReplyModal) { setTimeout(() => { if (textRef.current) { textRef.current.focus(); } }, 0); } }, [showReplyModal]); useEffect(() => { if (textRef.current) { const len = textRef.current.value.length; textRef.current.setSelectionRange(len, len); } }, []); const contentPrefix = `c/${parentCid && Plebbit.getShortCid(parentCid)}\n`; const handleContentInput = (e: React.ChangeEvent) => { const { value } = e.target; if (!value.startsWith(contentPrefix)) { e.target.value = contentPrefix + value.slice(contentPrefix.length); } }; const handleContentChange = (e: React.ChangeEvent) => { const contentWithoutPrefix = e.target.value.slice(contentPrefix.length); const formattedContent = formatMarkdown(contentWithoutPrefix); if (textRef.current && textRef.current.value !== formattedContent) { setPublishReplyOptions({ content: formattedContent }); } }; const modalContent = (
{t('reply_to_cid', { cid: `c/${parentCid && Plebbit.getShortCid(parentCid)}`, interpolation: { escapeValue: false } })}
{ setAccount({ ...account, author: { ...account?.author, displayName: e.target.value } }); setPublishReplyOptions({ displayName: e.target.value || undefined }); }} />
{ setUrl(e.target.value); setPublishReplyOptions({ link: e.target.value || undefined }); }} />