From 67e3bc8d685711e042cb76f28e49e0ed7b88c07f Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Thu, 13 Jun 2024 17:50:05 +0200 Subject: [PATCH] feat: hide button for posts soft hides them persistently without blocking the cid --- src/components/post-desktop/post-desktop.tsx | 11 ++-- .../post-menu-desktop/post-menu-desktop.tsx | 31 ++++++++-- src/hooks/use-hide.ts | 62 +++++++++++++++++++ src/views/board/board.tsx | 31 +++++++++- 4 files changed, 123 insertions(+), 12 deletions(-) diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 92588def..3f87f6be 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -1,14 +1,15 @@ import { useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { Link, useLocation, useParams } from 'react-router-dom'; -import { Comment, useAccount, useBlock, useComment } from '@plebbit/plebbit-react-hooks'; +import { Comment, useAccount, useComment } from '@plebbit/plebbit-react-hooks'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import styles from '../../views/post/post.module.css'; import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils'; import { isValidURL } from '../../lib/utils/url-utils'; import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; -import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useEditCommentPrivileges from '../../hooks/use-author-privileges'; +import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; +import useHide from '../../hooks/use-hide'; import useReplies from '../../hooks/use-replies'; import useStateString from '../../hooks/use-state-string'; import CommentMedia from '../comment-media'; @@ -220,8 +221,8 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies const isInPendingPostView = isPendingPostView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params); - const { blocked, unblock, block } = useBlock({ cid }); - const isHidden = blocked && !isInPostPageView; + const { hidden, unhide, hide } = useHide({ cid }); + const isHidden = hidden && !isInPostPageView; const replies = useReplies(post); const visiblelinksCount = useCountLinksInReplies(post, 5); @@ -250,7 +251,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
{!isInPostPageView && !isDescription && !isRules && showReplies && ( - + )} {link && !isHidden && isValidURL(link) && } diff --git a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx index 974f34e1..e305831b 100644 --- a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx +++ b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx @@ -8,6 +8,7 @@ import styles from './post-menu-desktop.module.css'; import { getCommentMediaInfo } from '../../../lib/utils/media-utils'; import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-utils'; import { isAllView, isCatalogView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils'; +import useHide from '../../../hooks/use-hide'; import _ from 'lodash'; interface PostMenuDesktopProps { @@ -102,14 +103,34 @@ const ViewOnButton = ({ cid, isDescription, isInAllView, isInSubscriptionsView, ); }; +const BlockUserButton = ({ address }: { address: string }) => { + const { blocked, unblock, block } = useBlock({ address }); + + return ( +
+ {blocked ? 'Unblock' : 'Block'} user +
+ ); +}; + +const BlockBoardButton = ({ address }: { address: string }) => { + const { blocked, unblock, block } = useBlock({ address }); + + return ( +
+ {blocked ? 'Unblock' : 'Block'} board +
+ ); +}; + const PostMenuDesktop = ({ post }: { post: Comment }) => { const { t } = useTranslation(); - const { cid, isDescription, isRules, link, postCid, subplebbitAddress } = post || {}; + const { author, cid, isDescription, isRules, link, postCid, subplebbitAddress } = post || {}; const commentMediaInfo = getCommentMediaInfo(post); const { thumbnail, type, url } = commentMediaInfo || {}; const [menuBtnRotated, setMenuBtnRotated] = useState(false); - const { blocked, unblock, block } = useBlock({ cid }); + const { hidden, unhide, hide } = useHide({ cid }); const location = useLocation(); const params = useParams(); @@ -154,11 +175,11 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
{ - blocked ? unblock() : block(); + hidden ? unhide() : hide(); handleClose(); }} > - {blocked ? (postCid === cid ? t('unhide_thread') : t('unhide_post')) : postCid === cid ? t('hide_thread') : t('hide_post')} + {hidden ? (postCid === cid ? t('unhide_thread') : t('unhide_post')) : postCid === cid ? t('hide_thread') : t('hide_post')}
)} {link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && } @@ -171,6 +192,8 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => { subplebbitAddress={subplebbitAddress} onClose={handleClose} /> + + {(isInAllView || isInSubscriptionsView) && }
, document.body, diff --git a/src/hooks/use-hide.ts b/src/hooks/use-hide.ts index 9faef7c3..2f19c039 100644 --- a/src/hooks/use-hide.ts +++ b/src/hooks/use-hide.ts @@ -1 +1,63 @@ +import { useEffect, useCallback } from 'react'; +import { create } from 'zustand'; import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js'; + +interface HideStoreState { + hiddenCids: { [key: string]: boolean }; + hide: (cid: string) => void; + unhide: (cid: string) => void; +} + +const hideStore = localForageLru.createInstance({ + name: 'hideStore', + size: 1000, +}); + +const useHideStore = create((set) => ({ + hiddenCids: {}, + hide: (cid: string) => { + set((state) => ({ + hiddenCids: { ...state.hiddenCids, [cid]: true }, + })); + hideStore.setItem(cid, true); + }, + unhide: (cid: string) => { + set((state) => { + const newHiddenCids = { ...state.hiddenCids }; + delete newHiddenCids[cid]; + return { hiddenCids: newHiddenCids }; + }); + hideStore.removeItem(cid); + }, +})); + +const initializeHideStore = async () => { + const entries: [string, boolean][] = await hideStore.entries(); + const hiddenCids: { [key: string]: boolean } = {}; + entries.forEach(([key, value]) => { + hiddenCids[key] = value; + }); + + useHideStore.setState((state) => ({ + hiddenCids: { ...hiddenCids, ...state.hiddenCids }, + })); +}; + +const useHide = ({ cid }: { cid: string }) => { + const hiddenCids = useHideStore((state) => state.hiddenCids); + const hide = useHideStore((state) => state.hide); + const unhide = useHideStore((state) => state.unhide); + + const hidden = !!hiddenCids[cid]; + + useEffect(() => { + initializeHideStore(); + }, []); + + const hideCallback = useCallback(() => hide(cid), [hide, cid]); + const unhideCallback = useCallback(() => unhide(cid), [unhide, cid]); + + return { hidden, hide: hideCallback, unhide: unhideCallback }; +}; + +export default useHide; diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx index 9a74c93e..b4f74a81 100644 --- a/src/views/board/board.tsx +++ b/src/views/board/board.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef } from 'react'; import { useLocation, useParams } from 'react-router-dom'; -import { useAccount, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks'; +import { useAccount, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { useTranslation } from 'react-i18next'; import styles from './board.module.css'; @@ -87,15 +87,40 @@ const Board = () => { ); + const { blocked, unblock } = useBlock({ address: subplebbitAddress }); + const Footer = () => { let footerContent; if (feed.length === 0) { - footerContent = t('no_posts'); + if (blocked) { + footerContent = 'you have blocked this board'; + } else { + footerContent = t('no_posts'); + } } if (hasMore || subplebbitAddresses.length === 0) { footerContent = loadingString; } - return
{footerContent}
; + return ( +
+ {footerContent} + {blocked && ( + <> +   [ + { + unblock(); + reset(); + }} + > + Unblock + + ] + + )} +
+ ); }; // save the last Virtuoso state to restore it when navigating back