feat(reply modal): greentext by selecting text

This commit is contained in:
Tom (plebeius.eth)
2024-06-26 18:20:25 +02:00
parent c8f62a754d
commit 972964deda
5 changed files with 38 additions and 10 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
{!cid ? ( {!cid ? (
<span className={styles.pendingCid}>{state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}</span> <span className={styles.pendingCid}>{state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}</span>
) : ( ) : (
<span className={styles.replyToPost} title={t('reply_to_post')} onClick={() => openReplyModal && openReplyModal(cid)}> <span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={() => openReplyModal && openReplyModal(cid)}>
{shortCid} {shortCid}
</span> </span>
)} )}
+1 -1
View File
@@ -89,7 +89,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
{!cid ? ( {!cid ? (
<span className={styles.pendingCid}>{state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}</span> <span className={styles.pendingCid}>{state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}</span>
) : ( ) : (
<span className={styles.replyToPost} title={t('reply_to_post')} onClick={() => openReplyModal && openReplyModal(cid)}> <span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={() => openReplyModal && openReplyModal(cid)}>
{shortCid} {shortCid}
</span> </span>
)} )}
+14 -6
View File
@@ -7,6 +7,7 @@ import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import { getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isValidURL } from '../../lib/utils/url-utils'; import { isValidURL } from '../../lib/utils/url-utils';
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils'; import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useSelectedTextStore from '../../stores/use-selected-text-store';
import useReply from '../../hooks/use-reply'; import useReply from '../../hooks/use-reply';
import useIsMobile from '../../hooks/use-is-mobile'; import useIsMobile from '../../hooks/use-is-mobile';
import styles from './reply-modal.module.css'; import styles from './reply-modal.module.css';
@@ -23,14 +24,12 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { subplebbitAddress } = useParams() as { subplebbitAddress: string }; const { subplebbitAddress } = useParams() as { subplebbitAddress: string };
const { setContent, resetContent, replyIndex, publishReply } = useReply({ cid: parentCid, subplebbitAddress }); const { setContent, resetContent, replyIndex, publishReply } = useReply({ cid: parentCid, subplebbitAddress });
const account = useAccount(); const account = useAccount();
const { displayName } = account?.author || {}; const { displayName } = account?.author || {};
const [url, setUrl] = useState(''); const [url, setUrl] = useState('');
const textRef = useRef<HTMLTextAreaElement | null>(null);
const textRef = useRef<HTMLTextAreaElement>(null);
const urlRef = useRef<HTMLInputElement>(null); const urlRef = useRef<HTMLInputElement>(null);
const { selectedText } = useSelectedTextStore();
const onPublishReply = () => { const onPublishReply = () => {
const currentContent = textRef.current?.value || ''; const currentContent = textRef.current?.value || '';
@@ -89,6 +88,15 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
) )
: `The subplebbit might be offline and publishing might fail.`; : `The subplebbit might be offline and publishing might fail.`;
const setTextRef = (ref: HTMLTextAreaElement | null) => {
if (ref) {
textRef.current = ref;
!isMobile && ref.focus();
const len = ref.value.length;
ref.setSelectionRange(len, len);
}
};
const modalContent = ( const modalContent = (
<div className={styles.container} ref={nodeRef}> <div className={styles.container} ref={nodeRef}>
<div className={`replyModalHandle ${styles.title}`}> <div className={`replyModalHandle ${styles.title}`}>
@@ -130,12 +138,12 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
cols={48} cols={48}
rows={3} rows={3}
wrap='soft' wrap='soft'
ref={textRef} ref={setTextRef}
defaultValue={selectedText}
onChange={(e) => { onChange={(e) => {
const content = e.target.value.replace(/\n/g, '\n\n'); const content = e.target.value.replace(/\n/g, '\n\n');
setContent.content(content); setContent.content(content);
}} }}
autoFocus={!isMobile} // autofocus causes auto scroll to top on mobile
/> />
</div> </div>
{!(isInAllView || isInSubscriptionsView) && offlineAlert} {!(isInAllView || isInSubscriptionsView) && offlineAlert}
+7 -2
View File
@@ -1,21 +1,26 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import useIsMobile from './use-is-mobile'; import useIsMobile from './use-is-mobile';
import useSelectedTextStore from '../stores/use-selected-text-store';
const useReplyModal = () => { const useReplyModal = () => {
const [showReplyModal, setShowReplyModal] = useState(false); const [showReplyModal, setShowReplyModal] = useState(false);
const [activeCid, setActiveCid] = useState<string | null>(null); const [activeCid, setActiveCid] = useState<string | null>(null);
const { resetSelectedText, setSelectedText } = useSelectedTextStore();
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position // on mobile, the position is absolute instead of fixed, so we need to calculate the top position
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [scrollY, setScrollY] = useState<number>(0); const [scrollY, setScrollY] = useState<number>(0);
const closeModal = useCallback(() => { const closeModal = useCallback(() => {
resetSelectedText();
setActiveCid(null); setActiveCid(null);
setShowReplyModal(false); setShowReplyModal(false);
}, []); }, [resetSelectedText]);
const openReplyModal = useCallback( const openReplyModal = useCallback(
(cid: string) => { (cid: string) => {
let text = document.getSelection()?.toString();
text && setSelectedText(`>${text}\n`);
if (isMobile) { if (isMobile) {
const currentScrollY = window.scrollY; const currentScrollY = window.scrollY;
setScrollY(currentScrollY); setScrollY(currentScrollY);
@@ -27,7 +32,7 @@ const useReplyModal = () => {
setShowReplyModal(true); setShowReplyModal(true);
} }
}, },
[activeCid, isMobile], [activeCid, isMobile, setSelectedText],
); );
return { activeCid, closeModal, openReplyModal, scrollY, showReplyModal }; return { activeCid, closeModal, openReplyModal, scrollY, showReplyModal };
+15
View File
@@ -0,0 +1,15 @@
import { create } from 'zustand';
interface SelectedTextState {
selectedText: string;
setSelectedText: (text: string) => void;
resetSelectedText: () => void;
}
const useSelectedTextStore = create<SelectedTextState>((set) => ({
selectedText: '',
setSelectedText: (text) => set({ selectedText: text }),
resetSelectedText: () => set({ selectedText: '' }),
}));
export default useSelectedTextStore;