2024-05-31 15:45:48 +02:00
import { useEffect , useRef , useState } from 'react' ;
2024-06-12 17:38:22 +02:00
import { useLocation , useParams } from 'react-router-dom' ;
2024-04-28 20:08:41 +02:00
import { useTranslation } from 'react-i18next' ;
import Draggable from 'react-draggable' ;
2024-06-17 17:50:26 +02:00
import { setAccount , useAccount , useSubplebbit } from '@plebbit/plebbit-react-hooks' ;
2024-04-28 20:08:41 +02:00
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js' ;
2024-06-08 21:33:56 +02:00
import { getFormattedTimeAgo } from '../../lib/utils/time-utils' ;
2024-04-28 20:08:41 +02:00
import { isValidURL } from '../../lib/utils/url-utils' ;
2024-06-12 17:38:22 +02:00
import { isAllView , isSubscriptionsView } from '../../lib/utils/view-utils' ;
2024-04-28 20:08:41 +02:00
import useReply from '../../hooks/use-reply' ;
2024-05-29 16:17:37 +02:00
import useIsMobile from '../../hooks/use-is-mobile' ;
2024-04-28 20:08:41 +02:00
import styles from './reply-modal.module.css' ;
2024-05-31 15:45:48 +02:00
import { LinkTypePreviewer } from '../post-form' ;
2024-04-28 20:08:41 +02:00
import _ from 'lodash' ;
interface ReplyModalProps {
closeModal : () => void ;
parentCid : string ;
2024-04-30 11:08:01 +02:00
scrollY : number ;
2024-04-28 20:08:41 +02:00
}
2024-04-30 11:08:01 +02:00
const ReplyModal = ({ closeModal , parentCid , scrollY } : ReplyModalProps ) => {
2024-04-28 20:08:41 +02:00
const { t } = useTranslation ();
const { subplebbitAddress } = useParams () as { subplebbitAddress : string };
const { setContent , resetContent , replyIndex , publishReply } = useReply ({ cid : parentCid , subplebbitAddress });
const account = useAccount ();
const { displayName } = account ? . author || {};
2024-05-31 15:45:48 +02:00
const [ url , setUrl ] = useState ( '' );
2024-04-28 20:08:41 +02:00
const textRef = useRef < HTMLTextAreaElement >( null );
const urlRef = useRef < HTMLInputElement >( null );
const onPublishReply = () => {
const currentContent = textRef . current ? . value || '' ;
const currentUrl = urlRef . current ? . value || '' ;
if ( ! currentContent . trim () && ! currentUrl ) {
alert ( `Cannot post empty comment` );
return ;
}
if ( currentUrl && ! isValidURL ( currentUrl )) {
alert ( 'The provided link is not a valid URL.' );
return ;
}
publishReply ();
};
useEffect (() => {
if ( typeof replyIndex === 'number' ) {
closeModal ();
resetContent ();
}
}, [ replyIndex , resetContent , closeModal ]);
2024-04-29 13:28:04 +02:00
const nodeRef = useRef < HTMLDivElement >( null );
2024-05-29 16:17:37 +02:00
const isMobile = useIsMobile ();
2024-04-28 20:08:41 +02:00
2024-04-29 13:28:04 +02:00
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position
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` ;
}
2024-04-30 11:08:01 +02:00
}, [ isMobile , scrollY ]);
2024-04-29 13:28:04 +02:00
2024-05-09 22:55:57 +02:00
const parentCidRef = useRef < HTMLSpanElement >( null );
useEffect (() => {
if ( parentCidRef . current && parentCidRef . current ) {
const cidWidth = parentCidRef . current . offsetWidth ;
parentCidRef . current . style . width = ` ${ cidWidth } px` ;
}
}, [ parentCid ]);
2024-06-12 17:38:22 +02:00
const location = useLocation ();
2024-06-17 12:17:36 +02:00
const isInAllView = isAllView ( location . pathname , useParams ());
const isInSubscriptionsView = isSubscriptionsView ( location . pathname , useParams ());
2024-06-08 21:33:56 +02:00
const subplebbit = useSubplebbit ({ subplebbitAddress });
const { updatedAt } = subplebbit || {};
const isBoardOffline = subplebbit ? . updatedAt && subplebbit . updatedAt < Date . now () / 1000 - 60 * 60 ;
const offlineAlert = updatedAt
? isBoardOffline && (
< div className = { styles . offlineBoard }>{ `Posts last synced ${ getFormattedTimeAgo ( updatedAt ) } , the subplebbit might be offline and publishing might fail.` }</ div >
)
: `The subplebbit might be offline and publishing might fail.` ;
2024-04-29 13:28:04 +02:00
const modalContent = (
< div className = { styles . container } ref = { nodeRef }>
2024-04-29 14:26:02 +02:00
< div className = { `replyModalHandle ${ styles . title } ` }>
{ t ( 'reply_to_cid' , { cid : `c/ ${ parentCid && Plebbit . getShortCid ( parentCid ) } ` , interpolation : { escapeValue : false } })}
< button
className = { styles . closeIcon }
onClick = {( e ) => {
e . stopPropagation ();
closeModal ();
}}
title = 'close'
/>
</ div >
< div className = { styles . replyForm }>
< div className = { styles . name }>
< input
type = 'text'
defaultValue = { displayName }
2024-06-05 22:18:45 +02:00
placeholder = { displayName ? undefined : _ . capitalize ( t ( 'name' ))}
2024-04-29 14:26:02 +02:00
onChange = {( e ) => setAccount ({ ... account , author : { ... account ? . author , displayName : e.target.value } })}
/>
</ div >
< div className = { styles . link }>
2024-05-31 15:45:48 +02:00
< input
type = 'text'
ref = { urlRef }
placeholder = { _ . capitalize ( t ( 'link' ))}
onChange = {( e ) => {
setUrl ( e . target . value );
setContent . link ( e . target . value );
}}
/>
2024-04-29 14:26:02 +02:00
</ div >
< div className = { styles . content }>
2024-05-14 22:43:21 +02:00
< span className = { styles . parentCid } ref = { parentCidRef }>
{ `c/ ${ parentCid && Plebbit . getShortCid ( parentCid ) } ` }
2024-05-09 22:55:57 +02:00
</ span >
2024-04-29 14:53:46 +02:00
< textarea
cols = { 48 }
2024-05-15 15:00:12 +02:00
rows = { 3 }
2024-04-29 14:53:46 +02:00
wrap = 'soft'
ref = { textRef }
2024-05-07 22:23:49 +02:00
onChange = {( e ) => {
const content = e . target . value . replace ( /\n/g , '\n\n' );
setContent . content ( content );
}}
2024-04-30 11:35:47 +02:00
autoFocus = { ! isMobile } // autofocus causes auto scroll to top on mobile
2024-04-29 14:53:46 +02:00
/>
2024-04-29 14:26:02 +02:00
</ div >
2024-06-12 17:38:22 +02:00
{ ! ( isInAllView || isInSubscriptionsView ) && offlineAlert }
2024-06-08 21:33:56 +02:00
< div className = { styles . offlineAlert }></ div >
2024-04-29 14:26:02 +02:00
< div className = { styles . footer }>
2024-05-31 15:45:48 +02:00
{ url && (
<>
2024-06-24 18:31:08 +02:00
{ t ( 'link_type' )} : < LinkTypePreviewer link = { url } />{ ' ' }
2024-05-31 15:45:48 +02:00
</>
)}
< span className = { styles . spoilerButton }>
[
< label >
< input type = 'checkbox' onChange = {( e ) => setContent . spoiler ( e . target . checked )} />
2024-05-31 17:29:19 +02:00
{ _ . capitalize ( t ( 'spoiler' ))} ?
2024-05-31 15:45:48 +02:00
</ label >
]
</ span >
2024-04-29 14:26:02 +02:00
< button onClick = { onPublishReply }>{ t ( 'reply' )}</ button >
</ div >
</ div >
2024-04-29 13:28:04 +02:00
</ div >
);
return isMobile ? (
modalContent
) : (
< Draggable handle = '.replyModalHandle' nodeRef = { nodeRef }>
{ modalContent }
2024-04-28 20:08:41 +02:00
</ Draggable >
);
};
export default ReplyModal ;