diff --git a/package.json b/package.json index 1e8359c5..6fee9988 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "mock-require": "3.0.3", "postcss": "8.4.21", "react": "18.2.0", + "react-confirm-alert": "^3.0.6", "react-content-loader": "6.2.1", "react-dom": "18.2.0", "react-draggable": "4.4.5", diff --git a/src/components/EditModal.jsx b/src/components/EditModal.jsx new file mode 100644 index 00000000..e92220ba --- /dev/null +++ b/src/components/EditModal.jsx @@ -0,0 +1,67 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { StyledModal } from './styled/ReplyModal.styled'; +import useGeneralStore from '../hooks/stores/useGeneralStore'; +import Modal from 'react-modal'; +import Draggable from 'react-draggable'; + + +const EditModal = ({ isOpen, closeModal, originalCommentContent }) => { + const { + selectedStyle, + } = useGeneralStore(state => state); + + const nodeRef = useRef(null); + const commentRef = useRef(); + + const [isMobile, setIsMobile] = useState(window.innerWidth <= 480); + + + useEffect(() => { + const handleResize = () => setIsMobile(window.innerWidth <= 480); + window.addEventListener('resize', handleResize); + + // Cleanup function + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [setIsMobile]); + + + + return ( + + + + + Edit Comment + closeModal()} title="close" /> + + + + + + + Save + + + + + + ); +}; + +Modal.setAppElement('#root'); + +export default EditModal; \ No newline at end of file diff --git a/src/components/styled/Thread.styled.jsx b/src/components/styled/Thread.styled.jsx index 2b64d75f..cfbd4af0 100644 --- a/src/components/styled/Thread.styled.jsx +++ b/src/components/styled/Thread.styled.jsx @@ -3204,4 +3204,21 @@ export const Footer = styled.div` return ''; } }} +`; + +export const AuthorDeleteAlert = styled.div` + .author-delete-alert { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px; + background: #fff; + border-radius: 5px; + box-shadow: 0 0 10px 0 rgba(0,0,0,.25); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } `; \ No newline at end of file diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx index d33baa9a..cf2033f2 100644 --- a/src/components/views/Thread.jsx +++ b/src/components/views/Thread.jsx @@ -1,13 +1,15 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Helmet } from 'react-helmet-async'; import { Link, useNavigate, useParams } from 'react-router-dom'; +import { confirmAlert } from 'react-confirm-alert'; import { Tooltip } from 'react-tooltip'; -import { useAccount, useAccountComments, useComment, usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; +import { useAccount, useAccountComments, useComment, usePublishComment, usePublishCommentEdit, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils' import { debounce } from 'lodash'; import useGeneralStore from '../../hooks/stores/useGeneralStore'; import { Container, NavBar, Header, Break, PostForm, PostFormTable, PostMenu } from '../styled/Board.styled'; -import { ReplyFormLink, TopBar, BottomBar, BoardForm, Footer } from '../styled/Thread.styled'; +import { ReplyFormLink, TopBar, BottomBar, BoardForm, Footer, AuthorDeleteAlert } from '../styled/Thread.styled'; +import EditModal from '../EditModal'; import ImageBanner from '../ImageBanner'; import ModerationModal from '../ModerationModal'; import OfflineIndicator from '../OfflineIndicator'; @@ -63,15 +65,20 @@ const Thread = () => { const replyMenuRefs = useRef({}); const [triggerPublishComment, setTriggerPublishComment] = useState(false); + const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false); const [deletePost, setDeletePost] = useState(false); const [errorMessage, setErrorMessage] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [isReplyOpen, setIsReplyOpen] = useState(false); + const [isEditModalOpen, setIsEditModalOpen] = useState(false); + const [originalCommentContent, setOriginalCommentContent] = useState(null); const [prevScrollPos, setPrevScrollPos] = useState(0); const [visible, setVisible] = useState(true); const [rotatedStates, setRotatedStates] = useState({}); const [isImageSearchOpen, setIsImageSearchOpen] = useState(false); const [isModerator, setIsModerator] = useState(false); + const [commentCid, setCommentCid] = useState(null); + const [editedComment, setEditedComment] = useState(null); useError(errorMessage, [errorMessage]); useSuccess(successMessage, [successMessage]); @@ -330,6 +337,82 @@ const Thread = () => { }; + const [publishCommentEditOptions, setPublishCommentEditOptions] = useState({ + commentCid: commentCid, + subplebbitAddress: selectedAddress, + onChallenge, + onChallengeVerification, + onError: (error) => { + setErrorMessage(error); + }, + }); + + + const {error, publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions); + + useEffect(() => { + if (error) { + setErrorMessage(error); + } + }, [error]); + + + const handleAuthorDeleteClick = (commentCid) => { + handleOptionClick(commentCid); + + confirmAlert({ + customUI: ({ onClose }) => { + return ( + + + Are you sure you want to delete this post? + No + { + setCommentCid(commentCid); + setPublishCommentEditOptions(prevOptions => ({ + ...prevOptions, + deleted: true, + })); + setTriggerPublishCommentEdit(true); + onClose(); + }} + > + Yes + + + + ); + } + }); + }; + + const handleAuthorEditClick = (comment) => { + handleOptionClick(comment.cid); + setCommentCid(comment.cid); + setOriginalCommentContent(comment.content); + setIsEditModalOpen(true); + } + + + useEffect(() => { + setPublishCommentEditOptions((prevOptions) => ({ + ...prevOptions, + commentCid: commentCid, + })); + }, [commentCid]); + + + useEffect(() => { + if (publishCommentEditOptions && triggerPublishCommentEdit) { + (async () => { + await publishCommentEdit(); + setTriggerPublishCommentEdit(false); + })(); + } + }, [publishCommentEditOptions, triggerPublishCommentEdit, publishCommentEdit]); + + // mobile navbar board select functionality const handleSelectChange = (event) => { const selected = event.target.value; @@ -374,6 +457,11 @@ const Thread = () => { isOpen={isModerationOpen} closeModal={() => setIsModerationOpen(false)} deletePost={deletePost} /> + setIsEditModalOpen(false)} + originalCommentContent={originalCommentContent} /> <> @@ -714,8 +802,8 @@ const Thread = () => { handleOptionClick(comment.cid)}>Hide thread {comment?.author?.shortAddress === account?.author?.shortAddress ? ( <> - handleOptionClick(comment.cid)}>Edit post - handleOptionClick(comment.cid)}>Delete post + handleAuthorEditClick(comment)}>Edit post + handleAuthorDeleteClick(comment.cid)}>Delete post > ) : null} {isModerator ? ( diff --git a/yarn.lock b/yarn.lock index 32ae17ff..d20f4e55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12105,6 +12105,11 @@ react-app-polyfill@^3.0.0: regenerator-runtime "^0.13.9" whatwg-fetch "^3.6.2" +react-confirm-alert@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/react-confirm-alert/-/react-confirm-alert-3.0.6.tgz#f7552ec839c1e189370cef02ffed3035e7ca2e22" + integrity sha512-rplP6Ed9ZSNd0KFV5BUzk4EPQ77BxsrayllBXGFuA8xPXc7sbBjgU5KUrNpl7aWFmP7mXRlVXfuy1IT5DbffYw== + react-content-loader@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-6.2.1.tgz#8feb733c2d2495002e1b216f13707f2b5f2a8ead"
Are you sure you want to delete this post?