mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
add author edits logic and initial UI
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
"mock-require": "3.0.3",
|
"mock-require": "3.0.3",
|
||||||
"postcss": "8.4.21",
|
"postcss": "8.4.21",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
"react-confirm-alert": "^3.0.6",
|
||||||
"react-content-loader": "6.2.1",
|
"react-content-loader": "6.2.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-draggable": "4.4.5",
|
"react-draggable": "4.4.5",
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<StyledModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onRequestClose={closeModal}
|
||||||
|
contentLabel="Edit Comment"
|
||||||
|
shouldCloseOnEsc={false}
|
||||||
|
shouldCloseOnOverlayClick={isMobile}
|
||||||
|
selectedStyle={selectedStyle}
|
||||||
|
style={isMobile ? ({ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}) : ({ overlay: { backgroundColor: "rgba(0,0,0,0)" }})}>
|
||||||
|
<Draggable handle=".modal-header" nodeRef={nodeRef} disabled={isMobile}>
|
||||||
|
<div className="modal-content" ref={nodeRef}>
|
||||||
|
<div className="modal-header">
|
||||||
|
Edit Comment
|
||||||
|
<button className="icon" onClick={() => closeModal()} title="close" />
|
||||||
|
</div>
|
||||||
|
<div id="form">
|
||||||
|
<div className="textarea-wrapper">
|
||||||
|
<textarea className="textarea"
|
||||||
|
rows="4"
|
||||||
|
placeholder="Comment"
|
||||||
|
defaultValue={originalCommentContent}
|
||||||
|
wrap="soft"
|
||||||
|
ref={commentRef} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="next">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Draggable>
|
||||||
|
</StyledModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Modal.setAppElement('#root');
|
||||||
|
|
||||||
|
export default EditModal;
|
||||||
@@ -3205,3 +3205,20 @@ export const Footer = styled.div`
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
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%);
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Helmet } from 'react-helmet-async';
|
import { Helmet } from 'react-helmet-async';
|
||||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { confirmAlert } from 'react-confirm-alert';
|
||||||
import { Tooltip } from 'react-tooltip';
|
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 { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||||
import { Container, NavBar, Header, Break, PostForm, PostFormTable, PostMenu } from '../styled/Board.styled';
|
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 ImageBanner from '../ImageBanner';
|
||||||
import ModerationModal from '../ModerationModal';
|
import ModerationModal from '../ModerationModal';
|
||||||
import OfflineIndicator from '../OfflineIndicator';
|
import OfflineIndicator from '../OfflineIndicator';
|
||||||
@@ -63,15 +65,20 @@ const Thread = () => {
|
|||||||
const replyMenuRefs = useRef({});
|
const replyMenuRefs = useRef({});
|
||||||
|
|
||||||
const [triggerPublishComment, setTriggerPublishComment] = useState(false);
|
const [triggerPublishComment, setTriggerPublishComment] = useState(false);
|
||||||
|
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
||||||
const [deletePost, setDeletePost] = useState(false);
|
const [deletePost, setDeletePost] = useState(false);
|
||||||
const [errorMessage, setErrorMessage] = useState(null);
|
const [errorMessage, setErrorMessage] = useState(null);
|
||||||
const [successMessage, setSuccessMessage] = useState(null);
|
const [successMessage, setSuccessMessage] = useState(null);
|
||||||
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
||||||
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
|
const [originalCommentContent, setOriginalCommentContent] = useState(null);
|
||||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
const [rotatedStates, setRotatedStates] = useState({});
|
const [rotatedStates, setRotatedStates] = useState({});
|
||||||
const [isImageSearchOpen, setIsImageSearchOpen] = useState(false);
|
const [isImageSearchOpen, setIsImageSearchOpen] = useState(false);
|
||||||
const [isModerator, setIsModerator] = useState(false);
|
const [isModerator, setIsModerator] = useState(false);
|
||||||
|
const [commentCid, setCommentCid] = useState(null);
|
||||||
|
const [editedComment, setEditedComment] = useState(null);
|
||||||
|
|
||||||
useError(errorMessage, [errorMessage]);
|
useError(errorMessage, [errorMessage]);
|
||||||
useSuccess(successMessage, [successMessage]);
|
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 (
|
||||||
|
<AuthorDeleteAlert selectedStyle={selectedStyle}>
|
||||||
|
<div className='author-delete-alert'>
|
||||||
|
<p>Are you sure you want to delete this post?</p>
|
||||||
|
<button onClick={onClose}>No</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setCommentCid(commentCid);
|
||||||
|
setPublishCommentEditOptions(prevOptions => ({
|
||||||
|
...prevOptions,
|
||||||
|
deleted: true,
|
||||||
|
}));
|
||||||
|
setTriggerPublishCommentEdit(true);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</AuthorDeleteAlert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
// mobile navbar board select functionality
|
||||||
const handleSelectChange = (event) => {
|
const handleSelectChange = (event) => {
|
||||||
const selected = event.target.value;
|
const selected = event.target.value;
|
||||||
@@ -374,6 +457,11 @@ const Thread = () => {
|
|||||||
isOpen={isModerationOpen}
|
isOpen={isModerationOpen}
|
||||||
closeModal={() => setIsModerationOpen(false)}
|
closeModal={() => setIsModerationOpen(false)}
|
||||||
deletePost={deletePost} />
|
deletePost={deletePost} />
|
||||||
|
<EditModal
|
||||||
|
selectedStyle={selectedStyle}
|
||||||
|
isOpen={isEditModalOpen}
|
||||||
|
closeModal={() => setIsEditModalOpen(false)}
|
||||||
|
originalCommentContent={originalCommentContent} />
|
||||||
<NavBar selectedStyle={selectedStyle}>
|
<NavBar selectedStyle={selectedStyle}>
|
||||||
<>
|
<>
|
||||||
<span className="boardList">
|
<span className="boardList">
|
||||||
@@ -714,8 +802,8 @@ const Thread = () => {
|
|||||||
<li onClick={() => handleOptionClick(comment.cid)}>Hide thread</li>
|
<li onClick={() => handleOptionClick(comment.cid)}>Hide thread</li>
|
||||||
{comment?.author?.shortAddress === account?.author?.shortAddress ? (
|
{comment?.author?.shortAddress === account?.author?.shortAddress ? (
|
||||||
<>
|
<>
|
||||||
<li onClick={() => handleOptionClick(comment.cid)}>Edit post</li>
|
<li onClick={() => handleAuthorEditClick(comment)}>Edit post</li>
|
||||||
<li onClick={() => handleOptionClick(comment.cid)}>Delete post</li>
|
<li onClick={() => handleAuthorDeleteClick(comment.cid)}>Delete post</li>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{isModerator ? (
|
{isModerator ? (
|
||||||
|
|||||||
@@ -12105,6 +12105,11 @@ react-app-polyfill@^3.0.0:
|
|||||||
regenerator-runtime "^0.13.9"
|
regenerator-runtime "^0.13.9"
|
||||||
whatwg-fetch "^3.6.2"
|
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:
|
react-content-loader@6.2.1:
|
||||||
version "6.2.1"
|
version "6.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-6.2.1.tgz#8feb733c2d2495002e1b216f13707f2b5f2a8ead"
|
resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-6.2.1.tgz#8feb733c2d2495002e1b216f13707f2b5f2a8ead"
|
||||||
|
|||||||
Reference in New Issue
Block a user