diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx
index 09a34523..5cf7dc01 100644
--- a/src/components/post/post.tsx
+++ b/src/components/post/post.tsx
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { Link, useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
-import { Role, useSubplebbit } from '@plebbit/plebbit-react-hooks';
+import { Role, useAccount, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { getFormattedDate } from '../../lib/utils/time-utils';
@@ -11,10 +11,13 @@ import useReplies from '../../hooks/use-replies';
import useStateString from '../../hooks/use-state-string';
import useWindowWidth from '../../hooks/use-window-width';
import styles from './post.module.css';
-import LoadingEllipsis from '../loading-ellipsis';
-import Markdown from '../markdown';
import CommentMedia from '../comment-media';
import { canEmbed } from '../embed';
+import LoadingEllipsis from '../loading-ellipsis';
+import Markdown from '../markdown';
+import ReplyModal from '../reply-modal';
+import _ from 'lodash';
+import { getDisplayMediaInfoType } from '../../lib/utils/media-utils';
interface PostProps {
index?: number;
@@ -22,9 +25,10 @@ interface PostProps {
reply?: any;
roles?: Role[];
showAllReplies?: boolean;
+ openReplyModal?: (cid: string) => void;
}
-const PostDesktop = ({ post, roles, showAllReplies }: PostProps) => {
+const PostDesktop = ({ openReplyModal, post, roles, showAllReplies }: PostProps) => {
const { t } = useTranslation();
const { author, cid, content, link, linkHeight, linkWidth, locked, pinned, postCid, replyCount, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
const { address, displayName, shortAddress } = author || {};
@@ -53,6 +57,10 @@ const PostDesktop = ({ post, roles, showAllReplies }: PostProps) => {
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
+ // pending reply by account is not yet published
+ const account = useAccount();
+ const accountShortAddress = account?.author?.shortAddress;
+
return (
@@ -115,7 +123,7 @@ const PostDesktop = ({ post, roles, showAllReplies }: PostProps) => {
{displayName || _.capitalize(t('anonymous'))}
{authorRole && ` ## Board ${authorRole}`}{' '}
- {!(isDescription || isRules) &&
(u/{shortAddress}) }
+ {!(isDescription || isRules) &&
(u/{shortAddress || accountShortAddress}) }
{getFormattedDate(timestamp)}
@@ -124,13 +132,13 @@ const PostDesktop = ({ post, roles, showAllReplies }: PostProps) => {
{!(isDescription || isRules) && (
- !cid && e.preventDefault()}>
+ !cid && e.preventDefault()}>
c/
{!cid ? (
{state === 'failed' ? 'Failed' : 'Pending'}
) : (
-
+ openReplyModal && openReplyModal(cid)}>
{shortCid}
)}
@@ -187,15 +195,15 @@ const PostDesktop = ({ post, roles, showAllReplies }: PostProps) => {
{!(pinned && !isInPostPage) &&
replies &&
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
-
);
};
-const ReplyDesktop = ({ reply, roles }: PostProps) => {
+const ReplyDesktop = ({ reply, roles, openReplyModal }: PostProps) => {
const { t } = useTranslation();
const { author, cid, content, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, state, subplebbitAddress, timestamp } = reply || {};
const { address, displayName, shortAddress } = author || {};
@@ -216,6 +224,10 @@ const ReplyDesktop = ({ reply, roles }: PostProps) => {
{stateString !== 'Failed' ? : stateString}
);
+ // pending reply by account is not yet published
+ const account = useAccount();
+ const accountShortAddress = account?.author?.shortAddress;
+
return (
{'>>'}
@@ -229,18 +241,18 @@ const ReplyDesktop = ({ reply, roles }: PostProps) => {
{displayName || _.capitalize(t('anonymous'))}
{authorRole && ` ## Board ${authorRole}`}{' '}
-
(u/{shortAddress})
+
(u/{shortAddress || accountShortAddress})
{getFormattedDate(timestamp)}
- !cid && e.preventDefault()}>
+ !cid && e.preventDefault()}>
c/
{!cid ? (
{state === 'failed' ? 'Failed' : 'Pending'}
) : (
-
+ openReplyModal && openReplyModal(cid)}>
{shortCid}
)}
@@ -314,7 +326,7 @@ const ReplyDesktop = ({ reply, roles }: PostProps) => {
>
)}
- {!cid && (
+ {!cid && state === 'pending' && (
<>
{loadingString}
@@ -327,7 +339,7 @@ const ReplyDesktop = ({ reply, roles }: PostProps) => {
);
};
-const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
+const PostMobile = ({ openReplyModal, post, roles, showAllReplies }: PostProps) => {
const { t } = useTranslation();
const { author, cid, content, link, linkHeight, linkWidth, locked, pinned, replyCount, shortCid, state, subplebbitAddress, timestamp, title } = post || {};
const { address, displayName, shortAddress } = author || {};
@@ -349,6 +361,10 @@ const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
const replies = useReplies(post);
+ // pending reply by account is not yet published
+ const account = useAccount();
+ const accountShortAddress = account?.author?.shortAddress;
+
return (
@@ -366,7 +382,7 @@ const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
{displayName || _.capitalize(t('anonymous'))}
{authorRole && ` ## Board ${authorRole}`}{' '}
- {!(isDescription || isRules) &&
(u/{shortAddress || address?.slice(0, 12) + '...'})}
+ {!(isDescription || isRules) &&
(u/{shortAddress || accountShortAddress})}
{pinned && (
@@ -394,7 +410,7 @@ const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
{!cid ? (
{state === 'failed' ? 'Failed' : 'Pending'}
) : (
-
+ openReplyModal && openReplyModal(cid)}>
{shortCid}
)}
@@ -441,7 +457,7 @@ const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
replies &&
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
-
+
))}
@@ -449,7 +465,7 @@ const PostMobile = ({ post, roles, showAllReplies }: PostProps) => {
);
};
-const ReplyMobile = ({ reply, roles }: PostProps) => {
+const ReplyMobile = ({ reply, roles, openReplyModal }: PostProps) => {
const { t } = useTranslation();
const { author, content, cid, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, state, subplebbitAddress, timestamp } = reply || {};
const { address, displayName, shortAddress } = author || {};
@@ -461,6 +477,15 @@ const ReplyMobile = ({ reply, roles }: PostProps) => {
const isReplyingToReply = postCid !== parentCid;
+ // pending reply by account is not yet published
+ const account = useAccount();
+ const accountShortAddress = account?.author?.shortAddress;
+
+ const stateString = useStateString(reply);
+ const loadingString = stateString && (
+
{stateString !== 'Failed' ? : stateString}
+ );
+
return (
@@ -472,7 +497,7 @@ const ReplyMobile = ({ reply, roles }: PostProps) => {
{displayName || _.capitalize(t('anonymous'))}
{authorRole && ` ## Board ${authorRole}`}{' '}
-
(u/{shortAddress})
+
(u/{shortAddress || accountShortAddress})
{pinned && (
@@ -489,7 +514,7 @@ const ReplyMobile = ({ reply, roles }: PostProps) => {
{!cid ? (
{state === 'failed' ? 'Failed' : 'Pending'}
) : (
-
+ openReplyModal && openReplyModal(cid)}>
{shortCid}
)}
@@ -516,6 +541,12 @@ const ReplyMobile = ({ reply, roles }: PostProps) => {
>
)}
+ {!cid && state === 'pending' && (
+ <>
+
+ {loadingString}
+ >
+ )}
)}
@@ -527,14 +558,36 @@ const ReplyMobile = ({ reply, roles }: PostProps) => {
const Post = ({ post, showAllReplies = false }: PostProps) => {
const subplebbit = useSubplebbit({ subplebbitAddress: post?.subplebbitAddress });
const isMobile = useWindowWidth() < 640;
+
+ const [showReplyModal, setShowReplyModal] = useState(false);
+ const [activeCid, setActiveCid] = useState
(null);
+
+ const openReplyModal = (cid: string) => {
+ if (activeCid && activeCid !== cid) {
+ closeModal();
+ setActiveCid(cid);
+ setShowReplyModal(true);
+ } else if (!activeCid) {
+ setActiveCid(cid);
+ setShowReplyModal(true);
+ } else {
+ return;
+ }
+ };
+
+ const closeModal = () => {
+ setActiveCid(null);
+ setShowReplyModal(false);
+ };
+
return (
- {showReplyModal &&
}
+ {showReplyModal && activeCid &&
}
{isMobile ? (
-
+
) : (
-
+
)}
diff --git a/src/components/reply-modal/index.ts b/src/components/reply-modal/index.ts
new file mode 100644
index 00000000..b6861856
--- /dev/null
+++ b/src/components/reply-modal/index.ts
@@ -0,0 +1 @@
+export { default } from './reply-modal';
diff --git a/src/components/reply-modal/reply-modal.module.css b/src/components/reply-modal/reply-modal.module.css
new file mode 100644
index 00000000..5acd55f6
--- /dev/null
+++ b/src/components/reply-modal/reply-modal.module.css
@@ -0,0 +1,60 @@
+.container {
+ position: fixed;
+ top: calc(50% - 150px);
+ left: calc(50% - 150px);
+ display: block;
+ padding: 2px;
+ font-size: 10pt;
+ border-left: none;
+ border-top: none;
+ z-index: 1000;
+ background-color: var(--challenge-modal-background-color);
+ border: var(--challenge-modal-border);
+}
+
+.title {
+ font-size: var(--challenge-modal-title-font-size);
+ text-align: center;
+ margin-bottom: 1px;
+ padding: 0;
+ height: 18px;
+ line-height: 18px;
+ cursor: move;
+ font-weight: var(--challenge-modal-title-font-weight);
+ background-color: var(--challenge-modal-title-background-color);
+ color: var(--challenge-modal-title-text-color);
+ border: var(--challenge-modal-title-border);
+}
+
+.closeIcon {
+ all: unset;
+ float: right;
+ cursor: pointer;
+ margin-bottom: -4px;
+ width: 18px;
+ height: 18px;
+ border: none;
+ image-rendering: pixelated;
+ background-image: var(--close-button-background-image);
+}
+
+.container input, .container textarea {
+ border: 1px solid #aaa;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 10pt;
+ outline: medium none;
+ width: 298px;
+ filter: var(--filter80);
+ padding: 2px;
+ margin-bottom: 1px;
+}
+
+.content textarea {
+ vertical-align: top;
+}
+
+.footer button {
+ text-transform: capitalize;
+ float: right;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx
new file mode 100644
index 00000000..eda69f8d
--- /dev/null
+++ b/src/components/reply-modal/reply-modal.tsx
@@ -0,0 +1,80 @@
+import { useEffect, useRef } from 'react';
+import { useParams } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import Draggable from 'react-draggable';
+import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
+import { useAccount } from '@plebbit/plebbit-react-hooks';
+import { isValidURL } from '../../lib/utils/url-utils';
+import useReply from '../../hooks/use-reply';
+import styles from './reply-modal.module.css';
+import _ from 'lodash';
+
+interface ReplyModalProps {
+ closeModal: () => void;
+ parentCid: string;
+}
+
+const ReplyModal = ({ closeModal, parentCid }: ReplyModalProps) => {
+ 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 || {};
+
+ const textRef = useRef(null);
+ const urlRef = useRef(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]);
+
+ // react-draggable requires a ref to the modal node
+ const nodeRef = useRef(null);
+
+ return (
+
+
+
+ Reply to c/{parentCid && Plebbit.getShortCid(parentCid)}
+
+
+
+
+
+
+
+ setContent.link(e.target.value)} />
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ReplyModal;