diff --git a/src/app.tsx b/src/app.tsx index 54b8333e..6265b752 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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 ( <> + {activeCid && threadCid && subplebbitAddress && ( + + )} ); diff --git a/src/components/catalog-row/catalog-row.tsx b/src/components/catalog-row/catalog-row.tsx index ef055523..dcf6d556 100644 --- a/src/components/catalog-row/catalog-row.tsx +++ b/src/components/catalog-row/catalog-row.tsx @@ -105,7 +105,6 @@ const CatalogPost = ({ post }: { post: Comment }) => { content, isDescription, isRules, - lastChildCid, link, linkHeight, linkWidth, diff --git a/src/components/challenge-modal/challenge-modal.module.css b/src/components/challenge-modal/challenge-modal.module.css index 2180e99d..8671aa7e 100644 --- a/src/components/challenge-modal/challenge-modal.module.css +++ b/src/components/challenge-modal/challenge-modal.module.css @@ -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 { diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 4b48173d..4aa2fb36 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -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; @@ -46,7 +47,7 @@ const useShowOmittedReplies = create((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) => {
{'>>'}
- + {link && !hidden && !(deleted || removed) && isValidURL(link) && ( { ); }; -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} /> )} - + {!isHidden && !content && !(deleted || removed) &&
} {!isHidden && }
@@ -443,7 +446,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies showReplies && (showAllReplies || showOmittedReplies[cid] ? replies : replies.slice(-5)).map((reply, index) => (
- +
))} {isDescription && subplebbit?.rules && subplebbit?.rules.length > 0 && ( diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx index e031dc12..3b2c10ce 100644 --- a/src/components/post-mobile/post-mobile.tsx +++ b/src/components/post-mobile/post-mobile.tsx @@ -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} > - + {!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && }
@@ -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() && } - +
{!isInPostView && !isInPendingPostView && showReplies && ( @@ -357,7 +360,7 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies = showReplies && (showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
- +
))} {showRules && ( diff --git a/src/components/reply-modal/reply-modal.module.css b/src/components/reply-modal/reply-modal.module.css index fcec066d..8174635f 100644 --- a/src/components/reply-modal/reply-modal.module.css +++ b/src/components/reply-modal/reply-modal.module.css @@ -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; diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx index 51cae25b..79a9cb35 100644 --- a/src/components/reply-modal/reply-modal.tsx +++ b/src/components/reply-modal/reply-modal.tsx @@ -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( diff --git a/src/stores/use-reply-modal-store.ts b/src/stores/use-reply-modal-store.ts new file mode 100644 index 00000000..612614dc --- /dev/null +++ b/src/stores/use-reply-modal-store.ts @@ -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((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; diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx index 2c2e99ff..3cbfdf47 100644 --- a/src/views/board/board.tsx +++ b/src/views/board/board.tsx @@ -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() &&
}
{location.pathname.endsWith('/settings') && } - {activeCid && threadCid && postSubplebbitAddress && ( - - )} {((description && description.length > 0) || isInAllView) && ( { increaseViewportBy={{ bottom: 1200, top: 1200 }} totalCount={combinedFeed.length} data={combinedFeed} - itemContent={(index, post) => } + itemContent={(index, post) => } useWindowScroll={true} components={{ Footer }} endReached={loadMore} diff --git a/src/views/post/post.tsx b/src/views/post/post.tsx index 39fadad2..9c1775e0 100644 --- a/src/views/post/post.tsx +++ b/src/views/post/post.tsx @@ -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
{isMobile ? ( - + ) : ( - + )}
@@ -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 (
{isInSettigsView && } - {activeCid && threadCid && postSubplebbitAddress && ( - - )} {/* TODO: remove this replyCount error once api supports scrolling replies pages */} {replyCount > 60 && Error: this thread has too many replies, some of them cannot be displayed right now.} {error && Error: {error?.message || error?.toString?.()}} @@ -119,7 +104,7 @@ const PostPage = () => { ) : isInRulesView ? ( ) : ( - + )}
);