mirror of
https://github.com/plebbit/seedit.git
synced 2026-06-11 17:46:27 -04:00
feat(use-reply): implement replying to a post
This commit is contained in:
85
src/hooks/use-reply.ts
Normal file
85
src/hooks/use-reply.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useMemo } from 'react';
|
||||
import { ChallengeVerification, Comment, PublishCommentOptions, usePublishComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { create } from 'zustand';
|
||||
import useChallengesStore from './use-challenges';
|
||||
import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils';
|
||||
|
||||
type SetReplyStoreData = {
|
||||
subplebbitAddress: string;
|
||||
parentCid: string;
|
||||
content: string | undefined;
|
||||
link: string | undefined;
|
||||
spoiler: boolean;
|
||||
};
|
||||
|
||||
type ReplyState = {
|
||||
content: { [parentCid: string]: string | undefined };
|
||||
link: { [parentCid: string]: string | undefined };
|
||||
spoiler: { [parentCid: string]: boolean | undefined };
|
||||
publishCommentOptions: PublishCommentOptions;
|
||||
setReplyStore: (data: SetReplyStoreData) => void;
|
||||
resetReplyStore: (parentCid: string) => void;
|
||||
};
|
||||
|
||||
const { addChallenge } = useChallengesStore.getState();
|
||||
|
||||
const useReplyStore = create<ReplyState>((set) => ({
|
||||
content: {},
|
||||
link: {},
|
||||
spoiler: {},
|
||||
publishCommentOptions: {},
|
||||
setReplyStore: (data: SetReplyStoreData) =>
|
||||
set((state) => {
|
||||
const { subplebbitAddress, parentCid, content, link, spoiler } = data;
|
||||
const publishCommentOptions = {
|
||||
subplebbitAddress,
|
||||
parentCid,
|
||||
content,
|
||||
link,
|
||||
spoiler,
|
||||
onChallenge: (...args: any) => addChallenge(args),
|
||||
onChallengeVerification: (challengeVerification: ChallengeVerification, comment: Comment) => {
|
||||
alertChallengeVerificationFailed(challengeVerification, comment);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error(error);
|
||||
alert(error.message);
|
||||
},
|
||||
};
|
||||
return {
|
||||
content: { ...state.content, [parentCid]: content },
|
||||
link: { ...state.link, [parentCid]: link },
|
||||
spoiler: { ...state.spoiler, [parentCid]: spoiler },
|
||||
publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: publishCommentOptions },
|
||||
};
|
||||
}),
|
||||
|
||||
resetReplyStore: (parentCid) =>
|
||||
set((state) => ({
|
||||
content: { ...state.content, [parentCid]: undefined },
|
||||
link: { ...state.link, [parentCid]: undefined },
|
||||
spoiler: { ...state.spoiler, [parentCid]: undefined },
|
||||
publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: undefined },
|
||||
})),
|
||||
}));
|
||||
|
||||
const useReply = (comment: Comment) => {
|
||||
const subplebbitAddress = comment?.subplebbitAddress;
|
||||
const parentCid = comment?.cid;
|
||||
const publishCommentOptions = useReplyStore((state) => state.publishCommentOptions[parentCid]);
|
||||
const setReplyStore = useReplyStore((state) => state.setReplyStore);
|
||||
const resetReplyStore = useReplyStore((state) => state.resetReplyStore);
|
||||
|
||||
const setContent = useMemo(
|
||||
() => (content: string | undefined, link: string | undefined, spoiler: boolean) => setReplyStore({ subplebbitAddress, parentCid, content, link, spoiler }),
|
||||
[subplebbitAddress, parentCid, setReplyStore, comment],
|
||||
);
|
||||
|
||||
const resetContent = useMemo(() => () => resetReplyStore(parentCid), [parentCid, resetReplyStore]);
|
||||
|
||||
const { index, publishComment } = usePublishComment(publishCommentOptions);
|
||||
|
||||
return { setContent, resetContent, replyIndex: index, publishReply: publishComment };
|
||||
};
|
||||
|
||||
export default useReply;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -7,19 +7,55 @@ import PostComponent from '../../components/post';
|
||||
import useReplies from '../../hooks/use-replies';
|
||||
import Reply from '../../components/reply';
|
||||
import useStateString from '../../hooks/use-state-string';
|
||||
import useReply from '../../hooks/use-reply';
|
||||
import { usePendingReplyCount } from '../../hooks/use-pending-replycount';
|
||||
|
||||
const Post = () => {
|
||||
const { t } = useTranslation();
|
||||
const { commentCid } = useParams();
|
||||
const comment = useComment({ commentCid });
|
||||
const { content, replyCount, subplebbitAddress, title } = comment || {};
|
||||
const { replyCount, subplebbitAddress, title } = comment || {};
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const replies = useReplies(comment).map((reply, index) => <Reply key={`${index}${reply.cid}`} reply={reply} />) || '';
|
||||
const postTitle = title?.slice(0, 40) || content?.slice(0, 40);
|
||||
const subplebbitTitle = subplebbit?.title || subplebbit?.shortAddress;
|
||||
const { t } = useTranslation();
|
||||
const stateString = useStateString(comment);
|
||||
const commentCount = replyCount === 0 ? t('no_comments') : replyCount === 1 ? t('one_comment') : t('all_comments', { count: replyCount });
|
||||
|
||||
const replies = useReplies(comment).map((reply, index) => <Reply key={`${index}${reply.cid}`} reply={reply} />) || '';
|
||||
const { setContent, resetContent, replyIndex, publishReply } = useReply(comment);
|
||||
|
||||
const postTitle = title?.slice(0, 40) || comment?.content?.slice(0, 40);
|
||||
const subplebbitTitle = subplebbit?.title || subplebbit?.shortAddress;
|
||||
|
||||
const textRef = useRef<HTMLTextAreaElement>(null);
|
||||
const urlRef = useRef<HTMLInputElement>(null);
|
||||
const spoilerRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const pendingReplyCount = usePendingReplyCount({ parentCommentCid: commentCid });
|
||||
const totalReplyCount = replyCount + pendingReplyCount;
|
||||
const commentCount = totalReplyCount === 0 ? t('no_comments') : totalReplyCount === 1 ? t('one_comment') : t('all_comments', { count: totalReplyCount });
|
||||
|
||||
const [readyToPublish, setReadyToPublish] = useState(false);
|
||||
|
||||
const onPublish = () => {
|
||||
const currentContent = textRef.current?.value || '';
|
||||
if (!currentContent.trim()) {
|
||||
alert(`missing content`);
|
||||
return;
|
||||
}
|
||||
setContent(textRef.current?.value || undefined, urlRef.current?.value || undefined, spoilerRef.current?.checked || false);
|
||||
setReadyToPublish(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (readyToPublish) {
|
||||
publishReply();
|
||||
setReadyToPublish(false);
|
||||
}
|
||||
}, [readyToPublish, publishReply]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof replyIndex === 'number') {
|
||||
resetContent();
|
||||
}
|
||||
}, [replyIndex, resetContent]);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${postTitle || ''}${postTitle && subplebbitTitle ? ' - ' : ''}${subplebbitTitle || ''}${postTitle || subplebbitTitle ? ' - seedit' : 'seedit'}`;
|
||||
@@ -45,14 +81,16 @@ const Post = () => {
|
||||
</div>
|
||||
<div className={styles.mdContainer}>
|
||||
<div className={styles.md}>
|
||||
<input className={styles.url} placeholder={`url (${t('optional')})`} />
|
||||
<input className={styles.url} ref={urlRef} placeholder={`url (${t('optional')})`} />
|
||||
<span className={styles.spoiler}>
|
||||
{t('spoiler')}: <input type='checkbox' className={styles.checkbox} />
|
||||
{t('spoiler')}: <input type='checkbox' className={styles.checkbox} ref={spoilerRef} />
|
||||
</span>
|
||||
<textarea className={styles.textarea} placeholder={t('text')} />
|
||||
<textarea className={styles.textarea} ref={textRef} placeholder={t('text')} />
|
||||
</div>
|
||||
<div className={styles.bottomArea}>
|
||||
<button className={styles.save}>{t('post_save')}</button>
|
||||
<button className={styles.save} onClick={onPublish}>
|
||||
{t('post_save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { PublishCommentOptions, usePublishComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { create } from 'zustand';
|
||||
import { isSubplebbitSubmitView } from '../../lib/utils/view-utils';
|
||||
@@ -14,7 +14,7 @@ type SubmitState = {
|
||||
title: string | undefined;
|
||||
content: string | undefined;
|
||||
link: string | undefined;
|
||||
publishCommentOptions: any;
|
||||
publishCommentOptions: PublishCommentOptions;
|
||||
setSubmitStore: (data: Partial<SubmitState>) => void;
|
||||
resetSubmitStore: () => void;
|
||||
};
|
||||
@@ -26,7 +26,7 @@ const useSubmitStore = create<SubmitState>((set) => ({
|
||||
title: undefined,
|
||||
content: undefined,
|
||||
link: undefined,
|
||||
publishCommentOptions: undefined,
|
||||
publishCommentOptions: {},
|
||||
setSubmitStore: ({ subplebbitAddress, title, content, link }) =>
|
||||
set((state) => {
|
||||
const nextState = { ...state };
|
||||
|
||||
Reference in New Issue
Block a user