mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
refactor replymodal to render it globally
This commit is contained in:
+15
-1
@@ -3,6 +3,8 @@ import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom'
|
||||
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { initSnow, removeSnow } from './lib/snow';
|
||||
import { isAllView, isSubscriptionsView } from './lib/utils/view-utils';
|
||||
import useReplyModalStore from './stores/use-reply-modal-store';
|
||||
import useSpecialThemeStore from './stores/use-special-theme-store';
|
||||
import useIsMobile from './hooks/use-is-mobile';
|
||||
import useTheme from './hooks/use-theme';
|
||||
import styles from './app.module.css';
|
||||
@@ -16,10 +18,10 @@ import Post from './views/post';
|
||||
import { DesktopBoardButtons, MobileBoardButtons } from './components/board-buttons';
|
||||
import BoardHeader from './components/board-header';
|
||||
import ChallengeModal from './components/challenge-modal';
|
||||
import ReplyModal from './components/reply-modal';
|
||||
import PostForm from './components/post-form';
|
||||
import SubplebbitStats from './components/subplebbit-stats';
|
||||
import TopBar from './components/topbar';
|
||||
import useSpecialThemeStore from './stores/use-special-theme-store';
|
||||
|
||||
const BoardLayout = () => {
|
||||
const { accountCommentIndex, subplebbitAddress } = useParams();
|
||||
@@ -87,9 +89,21 @@ const GlobalLayout = () => {
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const { activeCid, threadCid, subplebbitAddress, closeModal, showReplyModal, scrollY } = useReplyModalStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChallengeModal />
|
||||
{activeCid && threadCid && subplebbitAddress && (
|
||||
<ReplyModal
|
||||
closeModal={closeModal}
|
||||
parentCid={activeCid}
|
||||
postCid={threadCid}
|
||||
scrollY={scrollY}
|
||||
showReplyModal={showReplyModal}
|
||||
subplebbitAddress={subplebbitAddress}
|
||||
/>
|
||||
)}
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -105,7 +105,6 @@ const CatalogPost = ({ post }: { post: Comment }) => {
|
||||
content,
|
||||
isDescription,
|
||||
isRules,
|
||||
lastChildCid,
|
||||
link,
|
||||
linkHeight,
|
||||
linkWidth,
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
background-color: var(--challenge-modal-title-background-color);
|
||||
color: var(--challenge-modal-title-text-color);
|
||||
border: var(--challenge-modal-title-border);
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
|
||||
@@ -29,6 +29,7 @@ import { PostProps } from '../../views/post/post';
|
||||
import { create } from 'zustand';
|
||||
import _ from 'lodash';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
import useReplyModalStore from '../../stores/use-reply-modal-store';
|
||||
|
||||
interface ShowOmittedRepliesState {
|
||||
showOmittedReplies: Record<string, boolean>;
|
||||
@@ -46,7 +47,7 @@ const useShowOmittedReplies = create<ShowOmittedRepliesState>((set) => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }: PostProps) => {
|
||||
const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, deleted, locked, pinned, parentCid, postCid, reason, removed, shortCid, state, subplebbitAddress, timestamp } = post || {};
|
||||
const title = post?.title?.trim();
|
||||
@@ -76,6 +77,8 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
|
||||
const { hidden } = useHide(post);
|
||||
|
||||
const { openReplyModal } = useReplyModalStore();
|
||||
|
||||
const onReplyModalClick = () => {
|
||||
deleted
|
||||
? isReply
|
||||
@@ -302,7 +305,7 @@ const PostMedia = ({ commentMediaInfo, hasThumbnail, isDescription, isRules, spo
|
||||
);
|
||||
};
|
||||
|
||||
const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
|
||||
const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
|
||||
let post = reply;
|
||||
// handle pending mod or author edit
|
||||
const { editedComment } = useEditedComment({ comment: reply });
|
||||
@@ -323,7 +326,7 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
|
||||
<div className={styles.replyDesktop}>
|
||||
<div className={styles.sideArrows}>{'>>'}</div>
|
||||
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`} data-cid={cid} data-author-address={author?.shortAddress} data-post-cid={postCid}>
|
||||
<PostInfo openReplyModal={openReplyModal} post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} />
|
||||
<PostInfo post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} />
|
||||
{link && !hidden && !(deleted || removed) && isValidURL(link) && (
|
||||
<PostMedia
|
||||
commentMediaInfo={commentMediaInfo}
|
||||
@@ -344,7 +347,7 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies = true }: PostProps) => {
|
||||
const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, content, deleted, link, linkHeight, linkWidth, pinned, postCid, removed, spoiler, state, subplebbitAddress, thumbnailUrl, parentCid } = post || {};
|
||||
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||
@@ -412,7 +415,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
|
||||
parentCid={parentCid}
|
||||
/>
|
||||
)}
|
||||
<PostInfo isHidden={hidden} openReplyModal={openReplyModal} post={post} postReplyCount={replyCount} roles={roles} />
|
||||
<PostInfo isHidden={hidden} post={post} postReplyCount={replyCount} roles={roles} />
|
||||
{!isHidden && !content && !(deleted || removed) && <div className={styles.spacer} />}
|
||||
{!isHidden && <CommentContent comment={post} />}
|
||||
</div>
|
||||
@@ -443,7 +446,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
|
||||
showReplies &&
|
||||
(showAllReplies || showOmittedReplies[cid] ? replies : replies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply openReplyModal={openReplyModal} reply={reply} roles={roles} postReplyCount={replyCount} />
|
||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} />
|
||||
</div>
|
||||
))}
|
||||
{isDescription && subplebbit?.rules && subplebbit?.rules.length > 0 && (
|
||||
|
||||
@@ -24,8 +24,9 @@ import ReplyQuotePreview from '../reply-quote-preview';
|
||||
import Tooltip from '../tooltip';
|
||||
import { PostProps } from '../../views/post/post';
|
||||
import _ from 'lodash';
|
||||
import useReplyModalStore from '../../stores/use-reply-modal-store';
|
||||
|
||||
const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: PostProps) => {
|
||||
const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
author,
|
||||
@@ -75,6 +76,8 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
|
||||
const { hidden } = useHide(post);
|
||||
|
||||
const { openReplyModal } = useReplyModalStore();
|
||||
|
||||
const onReplyModalClick = () => {
|
||||
deleted
|
||||
? isReply
|
||||
@@ -249,7 +252,7 @@ const ReplyBacklinks = ({ post }: PostProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
|
||||
const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
|
||||
let post = reply;
|
||||
// handle pending mod or author edit
|
||||
const { editedComment } = useEditedComment({ comment: reply });
|
||||
@@ -269,7 +272,7 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
|
||||
data-author-address={author?.shortAddress}
|
||||
data-post-cid={postCid}
|
||||
>
|
||||
<PostInfoAndMedia openReplyModal={openReplyModal} post={post} postReplyCount={postReplyCount} roles={roles} />
|
||||
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} roles={roles} />
|
||||
{!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && <CommentContent comment={post} />}
|
||||
<ReplyBacklinks post={reply} />
|
||||
</div>
|
||||
@@ -278,7 +281,7 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies = true }: PostProps) => {
|
||||
const PostMobile = ({ post, roles, showAllReplies, showReplies = true }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, pinned, postCid, replyCount, state, subplebbitAddress } = post || {};
|
||||
const { isDescription, isRules } = post || {}; // custom properties, not from api
|
||||
@@ -333,7 +336,7 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
|
||||
data-post-cid={postCid}
|
||||
>
|
||||
{shouldShowSnow() && <img src={`${process.env.PUBLIC_URL}/assets/xmashat.gif`} className={styles.xmasHat} alt='' />}
|
||||
<PostInfoAndMedia openReplyModal={openReplyModal} post={post} postReplyCount={replyCount} roles={roles} />
|
||||
<PostInfoAndMedia post={post} postReplyCount={replyCount} roles={roles} />
|
||||
<CommentContent comment={post} />
|
||||
</div>
|
||||
{!isInPostView && !isInPendingPostView && showReplies && (
|
||||
@@ -357,7 +360,7 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
|
||||
showReplies &&
|
||||
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply openReplyModal={openReplyModal} postReplyCount={replyCount} reply={reply} roles={roles} />
|
||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} />
|
||||
</div>
|
||||
))}
|
||||
{showRules && (
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
font-size: 10pt;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
z-index: 3;
|
||||
z-index: 999;
|
||||
background-color: var(--challenge-modal-background-color);
|
||||
border: var(--challenge-modal-border);
|
||||
will-change: transform;
|
||||
|
||||
@@ -147,7 +147,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
|
||||
const [{ x, y }, api] = useSpring(() => ({
|
||||
x: window.innerWidth / 2 - 150,
|
||||
y: window.innerHeight / 2 - 400,
|
||||
y: window.innerHeight / 2 - 200,
|
||||
}));
|
||||
|
||||
const bind = useDrag(
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { create } from 'zustand';
|
||||
import useSelectedTextStore from './use-selected-text-store';
|
||||
|
||||
interface ReplyModalState {
|
||||
showReplyModal: boolean;
|
||||
activeCid: string | null;
|
||||
threadCid: string | null;
|
||||
subplebbitAddress: string | null;
|
||||
scrollY: number;
|
||||
closeModal: () => void;
|
||||
openReplyModal: (parentCid: string, postCid: string, subplebbitAddress: string) => void;
|
||||
}
|
||||
|
||||
const useReplyModalStore = create<ReplyModalState>((set, get) => ({
|
||||
showReplyModal: false,
|
||||
activeCid: null,
|
||||
threadCid: null,
|
||||
subplebbitAddress: null,
|
||||
scrollY: 0,
|
||||
|
||||
closeModal: () => {
|
||||
// Reset selected text if you're using that store
|
||||
useSelectedTextStore.getState().resetSelectedText();
|
||||
set({
|
||||
showReplyModal: false,
|
||||
activeCid: null,
|
||||
});
|
||||
},
|
||||
|
||||
openReplyModal: (parentCid, postCid, subplebbitAddress) => {
|
||||
// Get selected text
|
||||
const text = document.getSelection()?.toString();
|
||||
if (text) {
|
||||
useSelectedTextStore.getState().setSelectedText(`>${text}\n`);
|
||||
}
|
||||
|
||||
// Handle mobile scrollY
|
||||
const isMobile = window.innerWidth <= 768; // Simple check, adjust as needed
|
||||
const scrollY = isMobile ? window.scrollY : 0;
|
||||
|
||||
// Don't update if already open with different parent
|
||||
if (get().activeCid && get().activeCid !== parentCid) {
|
||||
return;
|
||||
}
|
||||
|
||||
set({
|
||||
activeCid: parentCid,
|
||||
threadCid: postCid,
|
||||
showReplyModal: true,
|
||||
subplebbitAddress,
|
||||
scrollY,
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
export default useReplyModalStore;
|
||||
@@ -9,14 +9,12 @@ import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-util
|
||||
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||
import useReplyModal from '../../hooks/use-reply-modal';
|
||||
import useTimeFilter from '../../hooks/use-time-filter';
|
||||
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
import useSortingStore from '../../stores/use-sorting-store';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import { Post } from '../post';
|
||||
import ReplyModal from '../../components/reply-modal';
|
||||
import SettingsModal from '../../components/settings-modal';
|
||||
import SubplebbitDescription from '../../components/subplebbit-description';
|
||||
import SubplebbitRules from '../../components/subplebbit-rules';
|
||||
@@ -116,8 +114,6 @@ const Board = () => {
|
||||
const { createdAt, description, error, rules, shortAddress, state, suggested } = subplebbit || {};
|
||||
const title = isInAllView ? t('all') : isInSubscriptionsView ? t('subscriptions') : subplebbit?.title;
|
||||
|
||||
const { activeCid, threadCid, closeModal, openReplyModal, showReplyModal, scrollY, subplebbitAddress: postSubplebbitAddress } = useReplyModal();
|
||||
|
||||
const { blocked, unblock } = useBlock({ address: subplebbitAddress });
|
||||
|
||||
const handleNewerPostsButtonClick = () => {
|
||||
@@ -272,16 +268,6 @@ const Board = () => {
|
||||
{shouldShowSnow() && <hr />}
|
||||
<div className={`${styles.content} ${shouldShowSnow() ? styles.garland : ''}`}>
|
||||
{location.pathname.endsWith('/settings') && <SettingsModal />}
|
||||
{activeCid && threadCid && postSubplebbitAddress && (
|
||||
<ReplyModal
|
||||
closeModal={closeModal}
|
||||
parentCid={activeCid}
|
||||
postCid={threadCid}
|
||||
scrollY={scrollY}
|
||||
showReplyModal={showReplyModal}
|
||||
subplebbitAddress={postSubplebbitAddress}
|
||||
/>
|
||||
)}
|
||||
{((description && description.length > 0) || isInAllView) && (
|
||||
<SubplebbitDescription
|
||||
avatarUrl={suggested?.avatarUrl}
|
||||
@@ -298,7 +284,7 @@ const Board = () => {
|
||||
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
||||
totalCount={combinedFeed.length}
|
||||
data={combinedFeed}
|
||||
itemContent={(index, post) => <Post index={index} post={post} openReplyModal={openReplyModal} />}
|
||||
itemContent={(index, post) => <Post index={index} post={post} />}
|
||||
useWindowScroll={true}
|
||||
components={{ Footer }}
|
||||
endReached={loadMore}
|
||||
|
||||
+4
-19
@@ -4,10 +4,8 @@ import { Comment, Role, useComment, useEditedComment, useSubplebbit } from '@ple
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { isAllView, isDescriptionView, isRulesView, isSettingsView } from '../../lib/utils/view-utils';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import useReplyModal from '../../hooks/use-reply-modal';
|
||||
import PostDesktop from '../../components/post-desktop';
|
||||
import PostMobile from '../../components/post-mobile';
|
||||
import ReplyModal from '../../components/reply-modal';
|
||||
import SettingsModal from '../../components/settings-modal';
|
||||
import SubplebbitDescription from '../../components/subplebbit-description';
|
||||
import SubplebbitRules from '../../components/subplebbit-rules';
|
||||
@@ -23,10 +21,9 @@ export interface PostProps {
|
||||
roles?: Role[];
|
||||
showAllReplies?: boolean;
|
||||
showReplies?: boolean;
|
||||
openReplyModal?: (parentCid: string, postCid: string, subplebbitAddress: string) => void;
|
||||
}
|
||||
|
||||
export const Post = ({ post, showAllReplies = false, showReplies = true, openReplyModal }: PostProps) => {
|
||||
export const Post = ({ post, showAllReplies = false, showReplies = true }: PostProps) => {
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress: post?.subplebbitAddress });
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@@ -42,9 +39,9 @@ export const Post = ({ post, showAllReplies = false, showReplies = true, openRep
|
||||
<div className={styles.thread}>
|
||||
<div className={styles.postContainer}>
|
||||
{isMobile ? (
|
||||
<PostMobile post={comment} roles={subplebbit?.roles} showAllReplies={showAllReplies} showReplies={showReplies} openReplyModal={openReplyModal} />
|
||||
<PostMobile post={comment} roles={subplebbit?.roles} showAllReplies={showAllReplies} showReplies={showReplies} />
|
||||
) : (
|
||||
<PostDesktop post={comment} roles={subplebbit?.roles} showAllReplies={showAllReplies} showReplies={showReplies} openReplyModal={openReplyModal} />
|
||||
<PostDesktop post={comment} roles={subplebbit?.roles} showAllReplies={showAllReplies} showReplies={showReplies} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,8 +61,6 @@ const PostPage = () => {
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const { createdAt, description, rules, shortAddress, suggested, title } = subplebbit;
|
||||
|
||||
const { activeCid, threadCid, closeModal, openReplyModal, showReplyModal, scrollY, subplebbitAddress: postSubplebbitAddress } = useReplyModal();
|
||||
|
||||
const comment = useComment({ commentCid });
|
||||
|
||||
// if the comment is a reply, return the post comment instead, then the reply will be highlighted in the thread
|
||||
@@ -93,16 +88,6 @@ const PostPage = () => {
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
{isInSettigsView && <SettingsModal />}
|
||||
{activeCid && threadCid && postSubplebbitAddress && (
|
||||
<ReplyModal
|
||||
closeModal={closeModal}
|
||||
parentCid={activeCid}
|
||||
postCid={threadCid}
|
||||
scrollY={scrollY}
|
||||
showReplyModal={showReplyModal}
|
||||
subplebbitAddress={postSubplebbitAddress}
|
||||
/>
|
||||
)}
|
||||
{/* TODO: remove this replyCount error once api supports scrolling replies pages */}
|
||||
{replyCount > 60 && <span className={styles.error}>Error: this thread has too many replies, some of them cannot be displayed right now.</span>}
|
||||
{error && <span className={styles.error}>Error: {error?.message || error?.toString?.()}</span>}
|
||||
@@ -119,7 +104,7 @@ const PostPage = () => {
|
||||
) : isInRulesView ? (
|
||||
<SubplebbitRules createdAt={createdAt} rules={rules} subplebbitAddress={subplebbitAddress} />
|
||||
) : (
|
||||
<Post post={post} showAllReplies={true} openReplyModal={openReplyModal} />
|
||||
<Post post={post} showAllReplies={true} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user