From 5bcfda6908301a06a1518a9d51f27648dc8f31dd Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 7 Sep 2024 13:45:31 +0200 Subject: [PATCH 01/15] fix(post): incorrect spacing on enlarged images --- src/components/comment-media/comment-media.module.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/comment-media/comment-media.module.css b/src/components/comment-media/comment-media.module.css index abca8390..67287ce1 100644 --- a/src/components/comment-media/comment-media.module.css +++ b/src/components/comment-media/comment-media.module.css @@ -80,6 +80,7 @@ .content img, .content video, .content iframe, .content audio { max-width: 100%; max-height: 100%; + object-fit: contain; } .content img { @@ -128,8 +129,15 @@ padding: 3px 20px 5px 20px; } +.mediaDesktopOp { + max-width: calc(100% - 40px); + box-sizing: border-box; +} + .mediaDesktopOp img, .mediaDesktopOp video, .mediaDesktopOp iframe, .mediaDesktopOp audio { - padding: 3px 20px 5px 0; + padding: 3px 20px 5px 20px; + max-width: 100%; + box-sizing: border-box; } .fileDeleted { From 4664206e77c0331cd827a4cfa1e400d4e2922105 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 7 Sep 2024 14:11:33 +0200 Subject: [PATCH 02/15] feat(post): show media dimensions if available --- .../comment-media/comment-media.tsx | 6 ++- src/components/post-desktop/post-desktop.tsx | 7 ++- src/lib/utils/media-utils.ts | 43 ++++++++++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/components/comment-media/comment-media.tsx b/src/components/comment-media/comment-media.tsx index ebdaf4ec..96464484 100644 --- a/src/components/comment-media/comment-media.tsx +++ b/src/components/comment-media/comment-media.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Comment } from '@plebbit/plebbit-react-hooks'; import styles from './comment-media.module.css'; -import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils'; +import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils'; import { getHostname } from '../../lib/utils/url-utils'; import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame'; import useIsMobile from '../../hooks/use-is-mobile'; @@ -109,6 +109,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => { const { thumbnail, type, url } = commentMediaInfo || {}; const isMobile = useIsMobile(); const mediaClass = isMobile ? styles.mediaMobile : isReply ? styles.mediaDesktopReply : styles.mediaDesktopOp; + const mediaDimensions = getMediaDimensions(commentMediaInfo); return ( @@ -128,7 +129,8 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => { {url && url.length > 30 ? url.slice(0, 30) + '...' : url} {' '} - ({getDisplayMediaInfoType(type, t)}) + ({getDisplayMediaInfoType(type, t)} + {mediaDimensions && `, ${mediaDimensions}`}) )} {isMobile && (type === 'iframe' || type === 'video' || type === 'audio') && ( diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 7b5ed3c9..a1b536c5 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -4,7 +4,7 @@ import { Link, useLocation, useParams } from 'react-router-dom'; import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment } 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 { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils'; import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils'; import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { isValidURL } from '../../lib/utils/url-utils'; @@ -229,6 +229,8 @@ const PostMedia = ({ post }: PostProps) => { const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const [showThumbnail, setShowThumbnail] = useState(true); + const mediaDimensions = getMediaDimensions(commentMediaInfo); + return (
@@ -236,7 +238,8 @@ const PostMedia = ({ post }: PostProps) => { {spoiler ? _.capitalize(t('spoiler')) : url && url.length > 30 ? url.slice(0, 30) + '...' : url} {' '} - ({type && _.lowerCase(getDisplayMediaInfoType(type, t))}) + ({type && _.lowerCase(getDisplayMediaInfoType(type, t))} + {mediaDimensions && `, ${mediaDimensions}`}) {!showThumbnail && (type === 'iframe' || type === 'video' || type === 'audio') && ( {' '} diff --git a/src/lib/utils/media-utils.ts b/src/lib/utils/media-utils.ts index dd676416..61f879a8 100644 --- a/src/lib/utils/media-utils.ts +++ b/src/lib/utils/media-utils.ts @@ -9,6 +9,7 @@ export interface CommentMediaInfo { type: string; thumbnail?: string; patternThumbnailUrl?: string; + post?: Comment; } export const getDisplayMediaInfoType = (type: string, t: any) => { @@ -106,8 +107,46 @@ export const getCommentMediaInfo = (comment: Comment): CommentMediaInfo | undefi } const linkInfo = comment.link ? getLinkMediaInfo(comment.link) : undefined; if (linkInfo) { - linkInfo.thumbnail = comment.thumbnailUrl || linkInfo.thumbnail; - return linkInfo; + return { + ...linkInfo, + thumbnail: comment.thumbnailUrl || linkInfo.thumbnail, + post: comment, + }; } return; }; + +export const getMediaDimensions = (commentMediaInfo: CommentMediaInfo | undefined): string => { + if (!commentMediaInfo) return ''; + + const { type, url, post } = commentMediaInfo; + + if (type === 'iframe' && url) { + const embedUrl = new URL(url); + if (canEmbed(embedUrl)) { + // hardcoded dimensions from embed.module.css + if (embedUrl.hostname.includes('youtube.com') || embedUrl.hostname.includes('youtu.be')) { + return '800x450'; + } else if (embedUrl.hostname.includes('instagram.com')) { + return '360x420'; + } else if (embedUrl.hostname.includes('reddit.com')) { + return '500x520'; + } else if (embedUrl.hostname.includes('tiktok.com')) { + return '400x780'; + } else if (embedUrl.hostname.includes('x.com') || embedUrl.hostname.includes('twitter.com')) { + return '550x580'; + } else if (embedUrl.hostname.includes('soundcloud.com')) { + return '700x166'; + } + } + } else if (type === 'audio') { + return '700x240'; // hardcoded dimensions from embed.module.css + } else if (type === 'image' || type === 'video' || type === 'gif') { + // media dimensions calculated by API + if (post?.linkWidth && post?.linkHeight) { + return `${post.linkWidth}x${post.linkHeight}`; + } + } + + return ''; +}; From 72f0c79922b2e1dec60de8e236d2094685561b9f Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 7 Sep 2024 18:11:38 +0200 Subject: [PATCH 03/15] fix(board): show description even if there are no posts --- src/views/board/board.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx index 290a5730..80b1ce95 100644 --- a/src/views/board/board.tsx +++ b/src/views/board/board.tsx @@ -168,19 +168,19 @@ const Board = () => { subplebbitAddress={postSubplebbitAddress} /> )} + {((description && description.length > 0) || isInAllView) && ( + + )} {feed.length !== 0 ? ( <> {rules && rules.length > 0 && } - {((description && description.length > 0) || isInAllView) && ( - - )} Date: Sun, 8 Sep 2024 10:41:08 +0200 Subject: [PATCH 04/15] style(post mobile): improve buttons positioning, add Update and Auto buttons for clarity --- src/app.tsx | 2 +- .../board-buttons/board-buttons.module.css | 26 +++++++++ .../board-buttons/board-buttons.tsx | 53 ++++++++++++++++++- src/components/post-form/post-form.module.css | 1 + src/components/post-form/post-form.tsx | 1 - 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index ce795174..0e8eb1ff 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -46,8 +46,8 @@ const BoardLayout = () => { {isMobile ? (subplebbitAddress || isInAllView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && ( <> - + ) : (subplebbitAddress || isInAllView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && ( diff --git a/src/components/board-buttons/board-buttons.module.css b/src/components/board-buttons/board-buttons.module.css index 08e92ef9..ddadba4a 100644 --- a/src/components/board-buttons/board-buttons.module.css +++ b/src/components/board-buttons/board-buttons.module.css @@ -11,6 +11,32 @@ margin: 5px 2px; } +.mobileBoardButtons .secondRow { + margin-top: -5px; + padding-bottom: 20px; +} + +.mobileBoardButtons { + margin-top: -3px; +} + +.mobileBoardButtons .autoCheckbox { + margin: 0 3px 0 0; + position: relative; + top: -2px; + border-radius: 0; + height: 10px; + width: 10px; +} + +.disabledButton { + opacity: 0.5; +} + +.disabledButton:hover { + cursor: not-allowed !important; +} + .mobileBoardButtons a, .desktopBoardButtons a { all: unset; } diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index 190710cf..2fa41cb7 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -11,6 +11,7 @@ import styles from './board-buttons.module.css'; import Tooltip from '../tooltip'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import _ from 'lodash'; +import useIsMobile from '../../hooks/use-is-mobile'; interface BoardButtonsProps { address?: string | undefined; @@ -85,6 +86,49 @@ const RefreshButton = () => { ); }; +const UpdateButton = () => { + const { t } = useTranslation(); + const isMobile = useIsMobile(); + + return ( + <> + {/* TODO: Implement update button once available in API */} + {isMobile ? ( + + ) : ( + + )} + + ); +}; + +const AutoButton = () => { + const { t } = useTranslation(); + const isMobile = useIsMobile(); + + return ( + <> + {isMobile ? ( + + ) : ( + + )} + + ); +}; + const SortOptions = () => { const { t } = useTranslation(); const { sortType, setSortType } = useSortingStore(); @@ -194,6 +238,10 @@ export const MobileBoardButtons = () => { +
+ + +
) : ( <> @@ -266,7 +314,10 @@ export const DesktopBoardButtons = () => { [ ] [ - ] + ] [ + + ] [ + ] diff --git a/src/components/post-form/post-form.module.css b/src/components/post-form/post-form.module.css index 82373d42..2124f76a 100644 --- a/src/components/post-form/post-form.module.css +++ b/src/components/post-form/post-form.module.css @@ -16,6 +16,7 @@ .postFormMobile { text-align: center; margin-top: -2px; + padding-bottom: 7px; } .closed { diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index ce19159b..29e367d6 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -449,7 +449,6 @@ const PostForm = () => { {showForm ? t('close_post_form') : isInPostView ? t('post_a_reply') : t('start_new_thread')} {showForm && setShowForm(false)} postCid={postCid} />} -
)}
From a7839e4e25022c43eee3eb84983017082c026060 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 8 Sep 2024 10:41:39 +0200 Subject: [PATCH 05/15] style(topbar): add Create and Vote for clarity, with "disabled" styling --- src/components/topbar/topbar.module.css | 5 +++++ src/components/topbar/topbar.tsx | 9 ++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/topbar/topbar.module.css b/src/components/topbar/topbar.module.css index e1a1ac4e..274ba2d5 100644 --- a/src/components/topbar/topbar.module.css +++ b/src/components/topbar/topbar.module.css @@ -31,6 +31,11 @@ margin-right: -7px; } +.disabledButton { + opacity: 0.5; + cursor: not-allowed !important; +} + .searchBar input[type="text"] { outline: none; margin: 2px; diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx index 27942ec6..f2fa6a39 100644 --- a/src/components/topbar/topbar.tsx +++ b/src/components/topbar/topbar.tsx @@ -85,28 +85,27 @@ const TopBarDesktop = () => { {renderSubplebbits(projectsSubs)} {renderSubplebbits(interestsSubs)} {renderSubplebbits(randomSubs)} - {renderSubplebbits(internationalSubs)} - {/* [ + {renderSubplebbits(internationalSubs)}[ { e.preventDefault(); - alert('work in progress'); }} > Create ] [ { e.preventDefault(); - alert('work in progress'); }} > Vote - ] */} + ] [{t('settings')}] [ From c1910c97888df4616cb765f7a00497796b2e03df Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 8 Sep 2024 13:39:27 +0200 Subject: [PATCH 06/15] fix(post): special characters in content could overflow --- src/components/tooltip/tooltip.module.css | 1 + src/views/post/post.module.css | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/components/tooltip/tooltip.module.css b/src/components/tooltip/tooltip.module.css index 109b8466..aab2cac9 100644 --- a/src/components/tooltip/tooltip.module.css +++ b/src/components/tooltip/tooltip.module.css @@ -9,6 +9,7 @@ white-space: pre-line; max-width: 400px; color: #fff; + overflow: hidden; text-align: center; } diff --git a/src/views/post/post.module.css b/src/views/post/post.module.css index 9ca8c0e5..9266596d 100644 --- a/src/views/post/post.module.css +++ b/src/views/post/post.module.css @@ -160,6 +160,7 @@ .postDesktop .postMessage, .replyDesktop .postMessage { padding: 1em 40px; + overflow: hidden; } .postDesktop .abbr { @@ -328,6 +329,7 @@ .postMobile .postMessage { padding: 10px; font-size: var(--post-mobile-content-font-size); + overflow: hidden; word-break: break-word; } From 6e472b2b17fb7b94790388567eb061e8f164a8e7 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 8 Sep 2024 14:47:52 +0200 Subject: [PATCH 07/15] perf(catalog): optimize filtered feed --- src/views/catalog/catalog.tsx | 68 ++++++++++++++++------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index ee7bc0fc..280064fb 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef, useCallback } from 'react'; import { useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Comment, useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks'; @@ -21,25 +21,6 @@ import styles from './catalog.module.css'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; -const threadsWithImagesFilter = (comment: Comment) => { - const commentMediaInfo = getCommentMediaInfo(comment); - const hasThumbnail = getHasThumbnail(commentMediaInfo, comment?.link); - return hasThumbnail; -}; - -const textFilter = (comment: Comment, filterItems: any[]) => { - if (!filterItems) return true; - if (filterItems.length === 0) return true; - const titleLower = comment?.title?.toLowerCase() || ''; - const contentLower = comment?.content?.toLowerCase() || ''; - - return filterItems.every((item) => { - if (!item.enabled) return true; - const pattern = item.text.toLowerCase(); - return !(titleLower.includes(pattern) || contentLower.includes(pattern)); - }); -}; - const Catalog = () => { const { t } = useTranslation(); const location = useLocation(); @@ -47,7 +28,7 @@ const Catalog = () => { const isInAllView = isAllView(location.pathname, useParams()); const defaultSubplebbits = useDefaultSubplebbits(); - const { showAdultBoards, showGoreBoards, showTextOnlyThreads, filterItems } = useCatalogFiltersStore(); + const { showAdultBoards, showGoreBoards } = useCatalogFiltersStore(); const account = useAccount(); const subscriptions = account?.subscriptions; @@ -76,31 +57,42 @@ const Catalog = () => { return [subplebbitAddress]; }, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, showAdultBoards, showGoreBoards]); + const { showTextOnlyThreads, filterItems } = useCatalogFiltersStore(); + + const filter = useCallback( + (comment: Comment) => { + if (!showTextOnlyThreads && !getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) { + return false; + } + + const title = comment?.title?.toLowerCase() || ''; + const content = comment?.content?.toLowerCase() || ''; + + return !filterItems + .filter((item) => item.enabled) + .some((item) => { + const text = item.text.toLowerCase(); + return title.includes(text) || content.includes(text); + }); + }, + [filterItems, showTextOnlyThreads], + ); + const { imageSize } = useCatalogStyleStore(); const columnWidth = imageSize === 'Large' ? 270 : 180; const columnCount = Math.floor(useWindowWidth() / columnWidth); - // postPerPage based on columnCount for optimized feed, dont change value after first render - // eslint-disable-next-line - const postsPerPage = useMemo(() => (columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25), []); + const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25; const { timeFilterSeconds } = useTimeFilter(); const { sortType } = useSortingStore(); - const feedOptions: any = useMemo( - () => ({ - subplebbitAddresses, - sortType, - postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage, - filter: (comment: Comment) => { - if (!showTextOnlyThreads && !threadsWithImagesFilter(comment)) { - return false; - } - return textFilter(comment, filterItems); - }, - }), - [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, showTextOnlyThreads, filterItems], - ); + const feedOptions: any = { + subplebbitAddresses, + sortType, + postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage, + filter, + }; if (isInAllView || isInSubscriptionsView) { feedOptions.newerThan = timeFilterSeconds; From 5f55adef07895371fa63c44376061dcaa9b067d7 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 8 Sep 2024 16:31:21 +0200 Subject: [PATCH 08/15] fix(theme): changing theme wouldn't work in pending post page --- src/hooks/use-theme.ts | 16 +++++++++++----- src/views/pending-post/pending-post.tsx | 3 --- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/hooks/use-theme.ts b/src/hooks/use-theme.ts index e5fea198..bcff9cb8 100644 --- a/src/hooks/use-theme.ts +++ b/src/hooks/use-theme.ts @@ -5,6 +5,7 @@ import useThemeStore from '../stores/use-theme-store'; import useDefaultSubplebbits from './use-default-subplebbits'; import useInitialTheme from './use-initial-theme'; import { nsfwTags } from '../views/home/home'; +import { useAccountComment } from '@plebbit/plebbit-react-hooks'; const themeClasses = ['yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon']; @@ -15,9 +16,14 @@ const updateThemeClass = (newTheme: string) => { } }; -const useTheme = (pendingPostSubplebbitAddress?: string): [string, (theme: string) => void] => { +const useTheme = (): [string, (theme: string) => void] => { const location = useLocation(); const params = useParams<{ subplebbitAddress: string }>(); + const pendingPostParams = useParams<{ accountCommentIndex?: string }>(); + const pendingPostCommentIndex = pendingPostParams?.accountCommentIndex ? parseInt(pendingPostParams.accountCommentIndex) : undefined; + const pendingPost = useAccountComment({ commentIndex: pendingPostCommentIndex }); + const pendingPostSubplebbitAddress = pendingPost?.subplebbitAddress; + const setThemeStore = useThemeStore((state) => state.setTheme); const getTheme = useThemeStore((state) => state.getTheme); const loadThemes = useThemeStore((state) => state.loadThemes); @@ -27,7 +33,7 @@ const useTheme = (pendingPostSubplebbitAddress?: string): [string, (theme: strin const [currentTheme, setCurrentTheme] = useState(initialTheme); const getCurrentTheme = useCallback(() => { - const subplebbitAddress = params?.subplebbitAddress; + const subplebbitAddress = params?.subplebbitAddress || pendingPostSubplebbitAddress; const isInAllView = isAllView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); @@ -44,7 +50,7 @@ const useTheme = (pendingPostSubplebbitAddress?: string): [string, (theme: strin } return storedTheme || initialTheme; - }, [location.pathname, params, getTheme, subplebbits, initialTheme]); + }, [location.pathname, params, getTheme, subplebbits, initialTheme, pendingPostSubplebbitAddress]); useEffect(() => { const newTheme = getCurrentTheme(); @@ -60,7 +66,7 @@ const useTheme = (pendingPostSubplebbitAddress?: string): [string, (theme: strin const setSubplebbitTheme = useCallback( async (newTheme: string) => { - const subplebbitAddress = params?.subplebbitAddress; + const subplebbitAddress = params?.subplebbitAddress || pendingPostSubplebbitAddress; const isInAllView = isAllView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); @@ -78,7 +84,7 @@ const useTheme = (pendingPostSubplebbitAddress?: string): [string, (theme: strin setCurrentTheme(newTheme); updateThemeClass(newTheme); }, - [location.pathname, params, setThemeStore, subplebbits], + [location.pathname, params, setThemeStore, subplebbits, pendingPostSubplebbitAddress], ); return [currentTheme, setSubplebbitTheme]; diff --git a/src/views/pending-post/pending-post.tsx b/src/views/pending-post/pending-post.tsx index 72980b28..fedea830 100644 --- a/src/views/pending-post/pending-post.tsx +++ b/src/views/pending-post/pending-post.tsx @@ -4,7 +4,6 @@ import { useAccountComment } from '@plebbit/plebbit-react-hooks'; import { isSettingsView } from '../../lib/utils/view-utils'; import { Post } from '../post'; import SettingsModal from '../../components/settings-modal'; -import useTheme from '../../hooks/use-theme'; const PendingPost = () => { const { accountCommentIndex } = useParams<{ accountCommentIndex?: string }>(); @@ -15,8 +14,6 @@ const PendingPost = () => { const params = useParams(); const isInSettingsView = isSettingsView(location.pathname, params); - useTheme(post?.subplebbitAddress); - useEffect(() => window.scrollTo(0, 0), []); useEffect(() => { From 7d2bd38f58d45e615a28b2fbd5a7ba6f58247d01 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 8 Sep 2024 16:58:25 +0200 Subject: [PATCH 09/15] fix(post): "(You)" wasn't appearing for comments published in anon mode by user --- src/components/board-buttons/board-buttons.module.css | 2 +- .../reply-quote-preview/reply-quote-preview.tsx | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/board-buttons/board-buttons.module.css b/src/components/board-buttons/board-buttons.module.css index ddadba4a..69664298 100644 --- a/src/components/board-buttons/board-buttons.module.css +++ b/src/components/board-buttons/board-buttons.module.css @@ -30,7 +30,7 @@ } .disabledButton { - opacity: 0.5; + opacity: 0.5 !important; } .disabledButton:hover { diff --git a/src/components/reply-quote-preview/reply-quote-preview.tsx b/src/components/reply-quote-preview/reply-quote-preview.tsx index 407aae8c..90982e4d 100644 --- a/src/components/reply-quote-preview/reply-quote-preview.tsx +++ b/src/components/reply-quote-preview/reply-quote-preview.tsx @@ -3,6 +3,7 @@ import { createPortal } from 'react-dom'; import { Link, useNavigate } from 'react-router-dom'; import { Comment, useAccount } from '@plebbit/plebbit-react-hooks'; import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floating-ui/react'; +import useAnonModeStore from '../../stores/use-anon-mode-store'; import useIsMobile from '../../hooks/use-is-mobile'; import styles from '../../views/post/post.module.css'; import { Post } from '../../views/post'; @@ -143,6 +144,8 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i ); const account = useAccount(); + const { getThreadSigner } = useAnonModeStore(); + const threadSigner = quotelinkReply?.postCid ? getThreadSigner(quotelinkReply?.postCid) : null; const replyQuotelink = ( <> @@ -155,7 +158,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i onClick={(e) => handleClick(e, quotelinkReply?.cid, quotelinkReply?.subplebbitAddress)} > {quotelinkReply?.shortCid && `c/${quotelinkReply?.shortCid}`} - {quotelinkReply?.author?.address === account?.author?.address && ' (You)'} + {(quotelinkReply?.author?.address === account?.author?.address || quotelinkReply?.author?.address === threadSigner?.address) && ' (You)'}
{hoveredCid === quotelinkReply?.cid && @@ -252,6 +255,8 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is ); const account = useAccount(); + const { getThreadSigner } = useAnonModeStore(); + const threadSigner = quotelinkReply?.postCid ? getThreadSigner(quotelinkReply?.postCid) : null; const replyQuotelink = ( <> @@ -262,7 +267,7 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)} > {quotelinkReply?.shortCid && `c/${quotelinkReply?.shortCid}`} - {quotelinkReply?.author?.address === account?.author?.address && ' (You)'} + {(quotelinkReply?.author?.address === account?.author?.address || quotelinkReply?.author?.address === threadSigner?.address) && ' (You)'}
{quotelinkReply?.shortCid && ( Date: Tue, 10 Sep 2024 15:27:43 +0200 Subject: [PATCH 10/15] fix(post): anon ID could be wrong while post is pending --- src/components/post-desktop/post-desktop.tsx | 7 +++- src/components/post-mobile/post-mobile.tsx | 7 +++- src/hooks/use-post-cid.ts | 43 ++++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 src/hooks/use-post-cid.ts diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index a1b536c5..11e521fe 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -16,6 +16,7 @@ import useAuthorAddressClick from '../../hooks/use-author-address-click'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame'; import useHide from '../../hooks/use-hide'; +import usePostCidForPendingPost from '../../hooks/use-post-cid'; import useReplies from '../../hooks/use-replies'; import useStateString from '../../hooks/use-state-string'; import CommentMedia from '../comment-media'; @@ -70,9 +71,11 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }: // comment.author.shortAddress is undefined while the comment publishing state is pending, use account instead // in anon mode, use the newly generated signer.address instead, which will be comment.author.address const { anonMode } = useAnonMode(); - const { currentAnonSignerAddress } = useAnonModeStore(); + const { getThreadSigner, currentAnonSignerAddress } = useAnonModeStore(); + const postCidForSigner = usePostCidForPendingPost(parentCid); + const anonSignerAddress = postCidForSigner ? getThreadSigner(postCidForSigner)?.address || currentAnonSignerAddress : null; const account = useAccount(); - const pendingShortAddress = anonMode ? currentAnonSignerAddress && Plebbit.getShortAddress(currentAnonSignerAddress) : account?.author?.shortAddress; + const pendingShortAddress = anonMode ? anonSignerAddress && Plebbit.getShortAddress(anonSignerAddress) : account?.author?.shortAddress; const handleUserAddressClick = useAuthorAddressClick(); const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length; diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx index fbe1b93d..660084b7 100644 --- a/src/components/post-mobile/post-mobile.tsx +++ b/src/components/post-mobile/post-mobile.tsx @@ -14,6 +14,7 @@ import useAnonMode from '../../hooks/use-anon-mode'; import useAuthorAddressClick from '../../hooks/use-author-address-click'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useHide from '../../hooks/use-hide'; +import usePostCidForPendingPost from '../../hooks/use-post-cid'; import useReplies from '../../hooks/use-replies'; import useStateString from '../../hooks/use-state-string'; import CommentMedia from '../comment-media'; @@ -51,9 +52,11 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P // comment.author.shortAddress is undefined while the comment publishing state is pending, use account instead // in anon mode, use the newly generated signer.address instead, which will be comment.author.address const { anonMode } = useAnonMode(); - const { currentAnonSignerAddress } = useAnonModeStore(); + const { getThreadSigner, currentAnonSignerAddress } = useAnonModeStore(); + const postCidForSigner = usePostCidForPendingPost(parentCid); + const anonSignerAddress = postCidForSigner ? getThreadSigner(postCidForSigner)?.address || currentAnonSignerAddress : null; const account = useAccount(); - const pendingShortAddress = anonMode ? currentAnonSignerAddress && Plebbit.getShortAddress(currentAnonSignerAddress) : account?.author?.shortAddress; + const pendingShortAddress = anonMode ? anonSignerAddress && Plebbit.getShortAddress(anonSignerAddress) : account?.author?.shortAddress; const stateString = useStateString(post); diff --git a/src/hooks/use-post-cid.ts b/src/hooks/use-post-cid.ts new file mode 100644 index 00000000..075d1808 --- /dev/null +++ b/src/hooks/use-post-cid.ts @@ -0,0 +1,43 @@ +import { useEffect, useReducer } from 'react'; +import { useComment } from '@plebbit/plebbit-react-hooks'; + +type State = { + resolvedPostCid: string | null; + currentParentCid: string | undefined; +}; + +type Action = { type: 'SET_POST_CID'; payload: string } | { type: 'SET_PARENT_CID'; payload: string }; + +const reducer = (state: State, action: Action): State => { + switch (action.type) { + case 'SET_POST_CID': + return { ...state, resolvedPostCid: action.payload }; + case 'SET_PARENT_CID': + return { ...state, currentParentCid: action.payload }; + default: + return state; + } +}; + +const usePostCidForPendingPost = (initialParentCid: string | undefined): string | null => { + const [state, dispatch] = useReducer(reducer, { + resolvedPostCid: null, + currentParentCid: initialParentCid, + }); + + const comment = useComment({ commentCid: state.currentParentCid }); + + useEffect(() => { + if (comment) { + if (comment.postCid) { + dispatch({ type: 'SET_POST_CID', payload: comment.postCid }); + } else if (comment.parentCid) { + dispatch({ type: 'SET_PARENT_CID', payload: comment.parentCid }); + } + } + }, [comment]); + + return state.resolvedPostCid; +}; + +export default usePostCidForPendingPost; From d45cc8b1260ae60d9424796e298401a33d9a2fda Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Tue, 10 Sep 2024 18:20:24 +0200 Subject: [PATCH 11/15] use zustand memoization for catalog filters, it fixes the useFeed bug with performance --- src/stores/use-catalog-filters-store.ts | 48 ++++++++++++++++++++++-- src/views/catalog/catalog.tsx | 50 ++++++++----------------- 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/stores/use-catalog-filters-store.ts b/src/stores/use-catalog-filters-store.ts index 543f7478..1ed9c862 100644 --- a/src/stores/use-catalog-filters-store.ts +++ b/src/stores/use-catalog-filters-store.ts @@ -1,5 +1,7 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; +import { Comment } from '@plebbit/plebbit-react-hooks'; +import { getCommentMediaInfo, getHasThumbnail } from '../lib/utils/media-utils'; interface FilterItem { text: string; @@ -16,15 +18,21 @@ interface CatalogFiltersStore { filterText: string; setFilterText: (value: string) => void; filterItems: FilterItem[]; - setFilterItems: (items: FilterItem[]) => void; // New method to set all filter items at once - saveAndApplyFilters: (items: FilterItem[]) => void; // Updated to accept items + setFilterItems: (items: FilterItem[]) => void; + saveAndApplyFilters: (items: FilterItem[]) => void; + filter: ((comment: Comment) => boolean) | undefined; + updateFilter: () => void; + initializeFilter: () => void; } const useCatalogFiltersStore = create( persist( - (set) => ({ + (set, get) => ({ showTextOnlyThreads: false, - setShowTextOnlyThreads: (value: boolean) => set({ showTextOnlyThreads: value }), + setShowTextOnlyThreads: (value: boolean) => { + set({ showTextOnlyThreads: value }); + get().updateFilter(); + }, showAdultBoards: false, setShowAdultBoards: (value: boolean) => set({ showAdultBoards: value }), showGoreBoards: false, @@ -39,12 +47,44 @@ const useCatalogFiltersStore = create( saveAndApplyFilters: (items: FilterItem[]) => { const nonEmptyItems = items.filter((item) => item.text.trim() !== ''); set({ filterItems: nonEmptyItems }); + get().updateFilter(); + }, + filter: undefined, + updateFilter: () => + set((state) => ({ + filter: (comment: Comment) => { + const { showTextOnlyThreads, filterItems } = state; + if (!showTextOnlyThreads && !getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) { + return false; + } + + const title = comment?.title?.toLowerCase() || ''; + const content = comment?.content?.toLowerCase() || ''; + + return !filterItems + .filter((item) => item.enabled) + .some((item) => { + const text = item.text.toLowerCase(); + return title.includes(text) || content.includes(text); + }); + }, + })), + initializeFilter: () => { + const { updateFilter } = get(); + updateFilter(); }, }), { name: 'catalog-filters-storage', + onRehydrateStorage: () => (state) => { + if (state) { + state.updateFilter(); + } + }, }, ), ); +useCatalogFiltersStore.getState().updateFilter(); + export default useCatalogFiltersStore; diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index 280064fb..5afc8eaa 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -1,9 +1,8 @@ -import { useEffect, useMemo, useRef, useCallback } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Comment, useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks'; +import { useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; -import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils'; import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows'; import useDefaultSubplebbits from '../../hooks/use-default-subplebbits'; @@ -28,7 +27,7 @@ const Catalog = () => { const isInAllView = isAllView(location.pathname, useParams()); const defaultSubplebbits = useDefaultSubplebbits(); - const { showAdultBoards, showGoreBoards } = useCatalogFiltersStore(); + const { filter, showAdultBoards, showGoreBoards } = useCatalogFiltersStore(); const account = useAccount(); const subscriptions = account?.subscriptions; @@ -57,27 +56,6 @@ const Catalog = () => { return [subplebbitAddress]; }, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, showAdultBoards, showGoreBoards]); - const { showTextOnlyThreads, filterItems } = useCatalogFiltersStore(); - - const filter = useCallback( - (comment: Comment) => { - if (!showTextOnlyThreads && !getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) { - return false; - } - - const title = comment?.title?.toLowerCase() || ''; - const content = comment?.content?.toLowerCase() || ''; - - return !filterItems - .filter((item) => item.enabled) - .some((item) => { - const text = item.text.toLowerCase(); - return title.includes(text) || content.includes(text); - }); - }, - [filterItems, showTextOnlyThreads], - ); - const { imageSize } = useCatalogStyleStore(); const columnWidth = imageSize === 'Large' ? 270 : 180; @@ -87,16 +65,20 @@ const Catalog = () => { const { timeFilterSeconds } = useTimeFilter(); const { sortType } = useSortingStore(); - const feedOptions: any = { - subplebbitAddresses, - sortType, - postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage, - filter, - }; + const feedOptions = useMemo(() => { + const options: any = { + subplebbitAddresses, + sortType, + postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage, + filter, + }; - if (isInAllView || isInSubscriptionsView) { - feedOptions.newerThan = timeFilterSeconds; - } + if (isInAllView || isInSubscriptionsView) { + options.newerThan = timeFilterSeconds; + } + + return options; + }, [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, timeFilterSeconds, filter]); const { feed, hasMore, loadMore, reset } = useFeed(feedOptions); From ea42d5d46c0633168a9556a13555151a2b28ab05 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Tue, 10 Sep 2024 18:49:15 +0200 Subject: [PATCH 12/15] fix(catalog filters): clicking "save" button didn't close the modal --- src/views/catalog/catalog-filters/catalog-filters.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/catalog/catalog-filters/catalog-filters.tsx b/src/views/catalog/catalog-filters/catalog-filters.tsx index ba2cd14a..c44aa579 100644 --- a/src/views/catalog/catalog-filters/catalog-filters.tsx +++ b/src/views/catalog/catalog-filters/catalog-filters.tsx @@ -5,7 +5,7 @@ import { isAllView, isCatalogView } from '../../../lib/utils/view-utils'; import useCatalogFiltersStore from '../../../stores/use-catalog-filters-store'; import styles from './catalog-filters.module.css'; -const FiltersTable = () => { +const FiltersTable = ({ onSave }: { onSave: () => void }) => { const { t } = useTranslation(); const { filterItems, saveAndApplyFilters } = useCatalogFiltersStore(); @@ -18,7 +18,8 @@ const FiltersTable = () => { const handleSave = useCallback(() => { const nonEmptyFilters = localFilterItems.filter((item) => item.text.trim() !== ''); saveAndApplyFilters(nonEmptyFilters); - }, [saveAndApplyFilters, localFilterItems]); + onSave(); + }, [saveAndApplyFilters, localFilterItems, onSave]); const updateLocalFilterItem = useCallback((index: number, item: any) => { setLocalFilterItems((prev) => prev.map((f, i) => (i === index ? item : f))); @@ -139,7 +140,7 @@ const FiltersModal = ({ closeModal }: { closeModal: () => void }) => {
)} - {isInCatalogView && } + {isInCatalogView && } ); From 319ff28b51fc5a538648f2fbcc417a6d68d7f68b Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Tue, 10 Sep 2024 18:57:48 +0200 Subject: [PATCH 13/15] fix(catalog filters): clicking "hide threads without images" would run the filter before clicking save --- .../catalog-filters/catalog-filters.tsx | 89 ++++++++++++++----- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/src/views/catalog/catalog-filters/catalog-filters.tsx b/src/views/catalog/catalog-filters/catalog-filters.tsx index c44aa579..683b8373 100644 --- a/src/views/catalog/catalog-filters/catalog-filters.tsx +++ b/src/views/catalog/catalog-filters/catalog-filters.tsx @@ -5,15 +5,21 @@ import { isAllView, isCatalogView } from '../../../lib/utils/view-utils'; import useCatalogFiltersStore from '../../../stores/use-catalog-filters-store'; import styles from './catalog-filters.module.css'; -const FiltersTable = ({ onSave }: { onSave: () => void }) => { +const FiltersTable = ({ + localFilterItems, + setLocalFilterItems, + onSave, +}: { + localFilterItems: any[]; + setLocalFilterItems: React.Dispatch>; + onSave: () => void; +}) => { const { t } = useTranslation(); - const { filterItems, saveAndApplyFilters } = useCatalogFiltersStore(); - - const [localFilterItems, setLocalFilterItems] = useState(filterItems); + const { saveAndApplyFilters } = useCatalogFiltersStore(); const handleAddFilter = useCallback(() => { setLocalFilterItems((prev) => [...prev, { text: '', enabled: true }]); - }, []); + }, [setLocalFilterItems]); const handleSave = useCallback(() => { const nonEmptyFilters = localFilterItems.filter((item) => item.text.trim() !== ''); @@ -21,22 +27,31 @@ const FiltersTable = ({ onSave }: { onSave: () => void }) => { onSave(); }, [saveAndApplyFilters, localFilterItems, onSave]); - const updateLocalFilterItem = useCallback((index: number, item: any) => { - setLocalFilterItems((prev) => prev.map((f, i) => (i === index ? item : f))); - }, []); + const updateLocalFilterItem = useCallback( + (index: number, item: any) => { + setLocalFilterItems((prev) => prev.map((f, i) => (i === index ? item : f))); + }, + [setLocalFilterItems], + ); - const removeLocalFilterItem = useCallback((index: number) => { - setLocalFilterItems((prev) => prev.filter((_, i) => i !== index)); - }, []); + const removeLocalFilterItem = useCallback( + (index: number) => { + setLocalFilterItems((prev) => prev.filter((_, i) => i !== index)); + }, + [setLocalFilterItems], + ); - const moveLocalFilterItemUp = useCallback((index: number) => { - if (index === 0) return; - setLocalFilterItems((prev) => { - const newItems = [...prev]; - [newItems[index - 1], newItems[index]] = [newItems[index], newItems[index - 1]]; - return newItems; - }); - }, []); + const moveLocalFilterItemUp = useCallback( + (index: number) => { + if (index === 0) return; + setLocalFilterItems((prev) => { + const newItems = [...prev]; + [newItems[index - 1], newItems[index]] = [newItems[index], newItems[index - 1]]; + return newItems; + }); + }, + [setLocalFilterItems], + ); return ( @@ -100,12 +115,38 @@ const FiltersTable = ({ onSave }: { onSave: () => void }) => { const FiltersModal = ({ closeModal }: { closeModal: () => void }) => { const { t } = useTranslation(); - const { showAdultBoards, setShowAdultBoards, showGoreBoards, setShowGoreBoards, showTextOnlyThreads, setShowTextOnlyThreads } = useCatalogFiltersStore(); + const { showAdultBoards, setShowAdultBoards, showGoreBoards, setShowGoreBoards, showTextOnlyThreads, setShowTextOnlyThreads, filterItems, saveAndApplyFilters } = + useCatalogFiltersStore(); + + const [localShowAdultBoards, setLocalShowAdultBoards] = useState(showAdultBoards); + const [localShowGoreBoards, setLocalShowGoreBoards] = useState(showGoreBoards); + const [localShowTextOnlyThreads, setLocalShowTextOnlyThreads] = useState(showTextOnlyThreads); + const [localFilterItems, setLocalFilterItems] = useState(filterItems); + const location = useLocation(); const params = useParams(); const isInCatalogView = isCatalogView(location.pathname, params); const isInAllView = isAllView(location.pathname, params); + const handleSave = useCallback(() => { + setShowAdultBoards(localShowAdultBoards); + setShowGoreBoards(localShowGoreBoards); + setShowTextOnlyThreads(localShowTextOnlyThreads); + const nonEmptyFilters = localFilterItems.filter((item) => item.text.trim() !== ''); + saveAndApplyFilters(nonEmptyFilters); + closeModal(); + }, [ + localShowAdultBoards, + localShowGoreBoards, + localShowTextOnlyThreads, + localFilterItems, + setShowAdultBoards, + setShowGoreBoards, + setShowTextOnlyThreads, + saveAndApplyFilters, + closeModal, + ]); + return ( <>
@@ -118,7 +159,7 @@ const FiltersModal = ({ closeModal }: { closeModal: () => void }) => { {isInCatalogView && (
@@ -127,20 +168,20 @@ const FiltersModal = ({ closeModal }: { closeModal: () => void }) => {
)}
- {isInCatalogView && } + {isInCatalogView && } ); From b38948bbc9c1cd1cc1b4f76b93031a4b7576c954 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Tue, 10 Sep 2024 19:16:13 +0200 Subject: [PATCH 14/15] feat(catalog filters): add "filtered threads" count --- .../board-buttons/board-buttons.module.css | 10 ++++ .../board-buttons/board-buttons.tsx | 30 ++++++++++++ src/stores/use-catalog-filters-store.ts | 46 +++++++++++++------ 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/components/board-buttons/board-buttons.module.css b/src/components/board-buttons/board-buttons.module.css index 69664298..ee4bbc79 100644 --- a/src/components/board-buttons/board-buttons.module.css +++ b/src/components/board-buttons/board-buttons.module.css @@ -79,6 +79,16 @@ padding-top: 15px; } +.filteredThreadsCount { + text-transform: none !important; +} + +.mobileBoardButtons .filteredThreadsCount { + display: block; + margin-top: 3px; + margin-bottom: 15px; +} + @media (max-width: 640px) { .desktopBoardButtons { display: none; diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index 2fa41cb7..c9deccc4 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -12,6 +12,8 @@ import Tooltip from '../tooltip'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import _ from 'lodash'; import useIsMobile from '../../hooks/use-is-mobile'; +import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; +import { useEffect } from 'react'; interface BoardButtonsProps { address?: string | undefined; @@ -231,6 +233,14 @@ export const MobileBoardButtons = () => { const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any }); const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress; + const { filteredCount, resetFilteredCount } = useCatalogFiltersStore(); + + useEffect(() => { + if (subplebbitAddress) { + resetFilteredCount(); + } + }, [subplebbitAddress, resetFilteredCount]); + return (
{isInPostView || isInPendingPostPage ? ( @@ -252,6 +262,12 @@ export const MobileBoardButtons = () => { )} {!(isInAllView || isInSubscriptionsView) && } + {isInCatalogView && filteredCount > 0 && ( + + {' '} + — Filtered threads: {filteredCount} + + )} {isInCatalogView && ( <>
@@ -305,6 +321,14 @@ export const DesktopBoardButtons = () => { const isInPostView = isPostPageView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); + const { filteredCount, resetFilteredCount } = useCatalogFiltersStore(); + + useEffect(() => { + if (subplebbitAddress) { + resetFilteredCount(); + } + }, [subplebbitAddress, resetFilteredCount]); + return ( <>
@@ -336,6 +360,12 @@ export const DesktopBoardButtons = () => { )} [] + {isInCatalogView && filteredCount > 0 && ( + + {' '} + — Filtered threads: {filteredCount} + + )} {isInCatalogView && ( <> diff --git a/src/stores/use-catalog-filters-store.ts b/src/stores/use-catalog-filters-store.ts index 1ed9c862..921d1b5b 100644 --- a/src/stores/use-catalog-filters-store.ts +++ b/src/stores/use-catalog-filters-store.ts @@ -23,6 +23,8 @@ interface CatalogFiltersStore { filter: ((comment: Comment) => boolean) | undefined; updateFilter: () => void; initializeFilter: () => void; + filteredCount: number; + resetFilteredCount: () => void; } const useCatalogFiltersStore = create( @@ -30,7 +32,7 @@ const useCatalogFiltersStore = create( (set, get) => ({ showTextOnlyThreads: false, setShowTextOnlyThreads: (value: boolean) => { - set({ showTextOnlyThreads: value }); + set({ showTextOnlyThreads: value, filteredCount: 0 }); get().updateFilter(); }, showAdultBoards: false, @@ -46,33 +48,47 @@ const useCatalogFiltersStore = create( }, saveAndApplyFilters: (items: FilterItem[]) => { const nonEmptyItems = items.filter((item) => item.text.trim() !== ''); - set({ filterItems: nonEmptyItems }); + set({ filterItems: nonEmptyItems, filteredCount: 0 }); get().updateFilter(); }, filter: undefined, - updateFilter: () => + updateFilter: () => { + const filteredCids = new Set(); set((state) => ({ filter: (comment: Comment) => { const { showTextOnlyThreads, filterItems } = state; - if (!showTextOnlyThreads && !getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) { - return false; + + const shouldShow = (() => { + if (!showTextOnlyThreads && !getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) { + return false; + } + + const title = comment?.title?.toLowerCase() || ''; + const content = comment?.content?.toLowerCase() || ''; + + return !filterItems + .filter((item) => item.enabled) + .some((item) => { + const text = item.text.toLowerCase(); + return title.includes(text) || content.includes(text); + }); + })(); + + if (!shouldShow && !filteredCids.has(comment.cid)) { + filteredCids.add(comment.cid); + set((state) => ({ filteredCount: state.filteredCount + 1 })); } - const title = comment?.title?.toLowerCase() || ''; - const content = comment?.content?.toLowerCase() || ''; - - return !filterItems - .filter((item) => item.enabled) - .some((item) => { - const text = item.text.toLowerCase(); - return title.includes(text) || content.includes(text); - }); + return shouldShow; }, - })), + })); + }, initializeFilter: () => { const { updateFilter } = get(); updateFilter(); }, + filteredCount: 0, + resetFilteredCount: () => set({ filteredCount: 0 }), }), { name: 'catalog-filters-storage', From ac232c430f29d5756da993d745cf4f5a23e802bc Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Tue, 10 Sep 2024 19:19:39 +0200 Subject: [PATCH 15/15] add translation --- public/translations/ar/default.json | 3 ++- public/translations/bn/default.json | 3 ++- public/translations/cs/default.json | 3 ++- public/translations/da/default.json | 3 ++- public/translations/de/default.json | 3 ++- public/translations/el/default.json | 3 ++- public/translations/en/default.json | 3 ++- public/translations/es/default.json | 3 ++- public/translations/fa/default.json | 3 ++- public/translations/fi/default.json | 3 ++- public/translations/fil/default.json | 3 ++- public/translations/fr/default.json | 3 ++- public/translations/he/default.json | 3 ++- public/translations/hi/default.json | 3 ++- public/translations/hu/default.json | 3 ++- public/translations/id/default.json | 3 ++- public/translations/it/default.json | 3 ++- public/translations/ja/default.json | 3 ++- public/translations/ko/default.json | 3 ++- public/translations/mr/default.json | 3 ++- public/translations/nl/default.json | 3 ++- public/translations/no/default.json | 3 ++- public/translations/pl/default.json | 3 ++- public/translations/pt/default.json | 3 ++- public/translations/ro/default.json | 3 ++- public/translations/ru/default.json | 3 ++- public/translations/sq/default.json | 3 ++- public/translations/sv/default.json | 3 ++- public/translations/te/default.json | 3 ++- public/translations/th/default.json | 3 ++- public/translations/tr/default.json | 3 ++- public/translations/uk/default.json | 3 ++- public/translations/ur/default.json | 3 ++- public/translations/vi/default.json | 3 ++- public/translations/zh/default.json | 3 ++- src/components/board-buttons/board-buttons.module.css | 4 ++++ src/components/board-buttons/board-buttons.tsx | 6 ++++-- 37 files changed, 78 insertions(+), 37 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index d90b8f8f..6d62118f 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -231,5 +231,6 @@ "pattern": "نمط", "enable": "تفعيل", "order": "ترتيب", - "text": "نص" + "text": "نص", + "filtered_threads": "المواضيع المفلترة" } \ No newline at end of file diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 1838abbb..db3b5750 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -231,5 +231,6 @@ "pattern": "প্যাটার্ন", "enable": "সক্রিয় করুন", "order": "অর্ডার", - "text": "টেক্সট" + "text": "টেক্সট", + "filtered_threads": "ফিল্টার করা থ্রেড" } \ No newline at end of file diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index e3fff362..845cde9f 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -231,5 +231,6 @@ "pattern": "Vzor", "enable": "Povolit", "order": "Pořadí", - "text": "Text" + "text": "Text", + "filtered_threads": "Filtr threads" } \ No newline at end of file diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 52db8104..a84f4637 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -231,5 +231,6 @@ "pattern": "Mønster", "enable": "Aktiver", "order": "Rækkefølge", - "text": "Tekst" + "text": "Tekst", + "filtered_threads": "Filtrerede tråde" } \ No newline at end of file diff --git a/public/translations/de/default.json b/public/translations/de/default.json index 8789fe4c..112c6fa8 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -231,5 +231,6 @@ "pattern": "Muster", "enable": "Aktivieren", "order": "Reihenfolge", - "text": "Text" + "text": "Text", + "filtered_threads": "Gefilterte Threads" } \ No newline at end of file diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 6bb0ec2c..311fd2be 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -231,5 +231,6 @@ "pattern": "Μοτίβο", "enable": "Ενεργοποιήστε", "order": "Σειρά", - "text": "Κείμενο" + "text": "Κείμενο", + "filtered_threads": "Φιλτραρισμένα θέματα" } \ No newline at end of file diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 774022eb..e75f74a3 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -231,5 +231,6 @@ "pattern": "Pattern", "enable": "Enable", "order": "Order", - "text": "Text" + "text": "Text", + "filtered_threads": "Filtered threads" } \ No newline at end of file diff --git a/public/translations/es/default.json b/public/translations/es/default.json index 925c8a00..dc97a8ef 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -231,5 +231,6 @@ "pattern": "Patrón", "enable": "Habilitar", "order": "Orden", - "text": "Texto" + "text": "Texto", + "filtered_threads": "Hilos filtrados" } \ No newline at end of file diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index 6f4737b1..c4985757 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -231,5 +231,6 @@ "pattern": "الگو", "enable": "فعال کردن", "order": "ترتیب", - "text": "متن" + "text": "متن", + "filtered_threads": "رشته‌های فیلتر شده" } \ No newline at end of file diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index c79041be..ed89eb7f 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -231,5 +231,6 @@ "pattern": "Malli", "enable": "Ota käyttöön", "order": "Järjestys", - "text": "Teksti" + "text": "Teksti", + "filtered_threads": "Suodatetut ketjut" } \ No newline at end of file diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 92d72bae..236265de 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -231,5 +231,6 @@ "pattern": "Pattern", "enable": "I-enable", "order": "Kaayusan", - "text": "Teksto" + "text": "Teksto", + "filtered_threads": "Na-filter na mga thread" } \ No newline at end of file diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index eb1696c8..9cda61cc 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -231,5 +231,6 @@ "pattern": "Modèle", "enable": "Activer", "order": "Ordre", - "text": "Texte" + "text": "Texte", + "filtered_threads": "Threads filtrés" } \ No newline at end of file diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 8192f3af..69017065 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -231,5 +231,6 @@ "pattern": "תבנית", "enable": "הפעל", "order": "סדר", - "text": "טקסט" + "text": "טקסט", + "filtered_threads": "אשכולות ממוינים" } \ No newline at end of file diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index 65126a05..f79e6d21 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -231,5 +231,6 @@ "pattern": "पैटर्न", "enable": "सक्षम करें", "order": "क्रम", - "text": "पाठ" + "text": "पाठ", + "filtered_threads": "फ़िल्टर किए गए थ्रेड्स" } \ No newline at end of file diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index b4ba5efa..6cff3556 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -231,5 +231,6 @@ "pattern": "Minta", "enable": "Engedélyez", "order": "Rendelés", - "text": "Szöveg" + "text": "Szöveg", + "filtered_threads": "Szűrt szálak" } \ No newline at end of file diff --git a/public/translations/id/default.json b/public/translations/id/default.json index e6ebd4d9..8ac959ec 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -231,5 +231,6 @@ "pattern": "Pola", "enable": "Aktifkan", "order": "Urutan", - "text": "Teks" + "text": "Teks", + "filtered_threads": "Thread yang difilter" } \ No newline at end of file diff --git a/public/translations/it/default.json b/public/translations/it/default.json index e7002042..07424503 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -231,5 +231,6 @@ "pattern": "Modello", "enable": "Abilita", "order": "Ordine", - "text": "Testo" + "text": "Testo", + "filtered_threads": "Thread filtrati" } \ No newline at end of file diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index 90594a77..f343c653 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -231,5 +231,6 @@ "pattern": "パターン", "enable": "有効にする", "order": "順序", - "text": "テキスト" + "text": "テキスト", + "filtered_threads": "フィルタリングされたスレッド" } \ No newline at end of file diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index d26698e8..232f3c3c 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -231,5 +231,6 @@ "pattern": "패턴", "enable": "사용", "order": "순서", - "text": "텍스트" + "text": "텍스트", + "filtered_threads": "필터링된 스레드" } \ No newline at end of file diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 966c6a06..9c652d20 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -231,5 +231,6 @@ "pattern": "पॅटर्न", "enable": "सक्षम करा", "order": "आदेश", - "text": "पाठ" + "text": "पाठ", + "filtered_threads": "फिल्टर केलेले ध糹रे" } \ No newline at end of file diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index b9f5337a..996ac291 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -231,5 +231,6 @@ "pattern": "Patroon", "enable": "Inschakelen", "order": "Volgorde", - "text": "Tekst" + "text": "Tekst", + "filtered_threads": "Gefilterde threads" } \ No newline at end of file diff --git a/public/translations/no/default.json b/public/translations/no/default.json index a7974ec4..39c41233 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -231,5 +231,6 @@ "pattern": "Mønster", "enable": "Aktiver", "order": "Rekkefølge", - "text": "Tekst" + "text": "Tekst", + "filtered_threads": "Filtrerte tråder" } \ No newline at end of file diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 0c5cddc2..9dac3e8a 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -231,5 +231,6 @@ "pattern": "Wzór", "enable": "Włącz", "order": "Kolejność", - "text": "Tekst" + "text": "Tekst", + "filtered_threads": "Filtrowane wątki" } \ No newline at end of file diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index b2f2b35d..62882f0d 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -231,5 +231,6 @@ "pattern": "Padrão", "enable": "Ativar", "order": "Ordem", - "text": "Texto" + "text": "Texto", + "filtered_threads": "Threads filtrados" } \ No newline at end of file diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 65037c55..d605b60f 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -231,5 +231,6 @@ "pattern": "Model", "enable": "Activare", "order": "Ordine", - "text": "Text" + "text": "Text", + "filtered_threads": "Fire filtrate" } \ No newline at end of file diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index 34062f79..163bfceb 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -231,5 +231,6 @@ "pattern": "Шаблон", "enable": "Включить", "order": "Порядок", - "text": "Текст" + "text": "Текст", + "filtered_threads": "Отфильтрованные темы" } \ No newline at end of file diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index fa3beff2..178fd403 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -231,5 +231,6 @@ "pattern": "Model", "enable": "Aktivizo", "order": "Rendit", - "text": "Tekst" + "text": "Tekst", + "filtered_threads": "Tema të filtruar" } \ No newline at end of file diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index 4aaaa577..dddabc73 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -231,5 +231,6 @@ "pattern": "Mönster", "enable": "Aktivera", "order": "Ordning", - "text": "Text" + "text": "Text", + "filtered_threads": "Filtrerade trådar" } \ No newline at end of file diff --git a/public/translations/te/default.json b/public/translations/te/default.json index 1578d263..0ec40ee5 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -231,5 +231,6 @@ "pattern": "ప్యాటర్న్", "enable": "చేర్చు", "order": "ఆర్డర్", - "text": "పాఠం" + "text": "పాఠం", + "filtered_threads": "ఫిల్టర్ చేయబడిన థ్రెడ్లు" } \ No newline at end of file diff --git a/public/translations/th/default.json b/public/translations/th/default.json index 233c6408..bd6871a2 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -231,5 +231,6 @@ "pattern": "รูปแบบ", "enable": "เปิดใช้งาน", "order": "ลำดับ", - "text": "ข้อความ" + "text": "ข้อความ", + "filtered_threads": "กระทู้ที่กรองแล้ว" } \ No newline at end of file diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 3a1939be..9cd9cf29 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -231,5 +231,6 @@ "pattern": "Desen", "enable": "Etkinleştir", "order": "Sıra", - "text": "Metin" + "text": "Metin", + "filtered_threads": "Filtrelenmiş başlıklar" } \ No newline at end of file diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index 2cf13783..88fbe0c6 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -231,5 +231,6 @@ "pattern": "Шаблон", "enable": "Увімкнути", "order": "Порядок", - "text": "Текст" + "text": "Текст", + "filtered_threads": "Відфільтровані треди" } \ No newline at end of file diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 162a3311..1aba0d29 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -231,5 +231,6 @@ "pattern": "نمونہ", "enable": "فعال کریں", "order": "ترتیب", - "text": "متن" + "text": "متن", + "filtered_threads": "فلٹر کیے گئے تھریڈز" } \ No newline at end of file diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index eba3ff84..2a06a18f 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -231,5 +231,6 @@ "pattern": "Mẫu", "enable": "Kích hoạt", "order": "Thứ tự", - "text": "Văn bản" + "text": "Văn bản", + "filtered_threads": "Các chủ đề đã lọc" } \ No newline at end of file diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 660b8320..8532e16d 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -231,5 +231,6 @@ "pattern": "模式", "enable": "启用", "order": "顺序", - "text": "文本" + "text": "文本", + "filtered_threads": "已过滤的帖子" } \ No newline at end of file diff --git a/src/components/board-buttons/board-buttons.module.css b/src/components/board-buttons/board-buttons.module.css index ee4bbc79..00c1e533 100644 --- a/src/components/board-buttons/board-buttons.module.css +++ b/src/components/board-buttons/board-buttons.module.css @@ -83,6 +83,10 @@ text-transform: none !important; } +.mobileBoardButtons { + text-transform: capitalize; +} + .mobileBoardButtons .filteredThreadsCount { display: block; margin-top: 3px; diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index c9deccc4..b95cae18 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -222,6 +222,7 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView }; export const MobileBoardButtons = () => { + const { t } = useTranslation(); const params = useParams(); const location = useLocation(); const isInAllView = isAllView(location.pathname, params); @@ -265,7 +266,7 @@ export const MobileBoardButtons = () => { {isInCatalogView && filteredCount > 0 && ( {' '} - — Filtered threads: {filteredCount} + — {t('filtered_threads')}: {filteredCount} )} {isInCatalogView && ( @@ -311,6 +312,7 @@ const PostPageStats = () => { }; export const DesktopBoardButtons = () => { + const { t } = useTranslation(); const params = useParams(); const location = useLocation(); const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any }); @@ -363,7 +365,7 @@ export const DesktopBoardButtons = () => { {isInCatalogView && filteredCount > 0 && ( {' '} - — Filtered threads: {filteredCount} + — {t('filtered_threads')}: {filteredCount} )}