From bb1b273c397cd52c3f38e97efe3c2db151951892 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 12:30:04 +0200 Subject: [PATCH 1/8] remove ext-name, it sucks it would return old/unknown extensions (such as `.arts`) as video or image for no reason --- package.json | 1 - src/lib/utils/media-utils.ts | 28 +++++++++++++++++----------- src/modules.d.ts | 2 -- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 3d8efc03..39a38060 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "cross-env": "7.0.3", "electron-context-menu": "3.3.0", "electron-is-dev": "2.0.0", - "ext-name": "5.0.0", "fs-extra": "11.3.0", "http-proxy": "1.18.1", "i18next": "23.5.1", diff --git a/src/lib/utils/media-utils.ts b/src/lib/utils/media-utils.ts index 816e98e9..6575b252 100644 --- a/src/lib/utils/media-utils.ts +++ b/src/lib/utils/media-utils.ts @@ -1,5 +1,4 @@ import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js'; -import extName from 'ext-name'; import { canEmbed } from '../../components/embed'; import memoize from 'memoizee'; import { isValidURL } from './url-utils'; @@ -72,6 +71,11 @@ const getPatternThumbnailUrl = (url: URL): string | undefined => { } }; +// Known media file extensions - only these will be classified as media files +const KNOWN_IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'tiff']; +const KNOWN_VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'avi', 'mkv', 'flv', 'wmv', 'm4v']; +const KNOWN_AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma']; + export const getLinkMediaInfo = memoize( (link: string): CommentMediaInfo | undefined => { if (!isValidURL(link)) { @@ -80,7 +84,6 @@ export const getLinkMediaInfo = memoize( const url = new URL(link); let patternThumbnailUrl: string | undefined; let type: string = 'webpage'; - let mime: string | undefined; if (url.pathname === '/_next/image' && url.search.startsWith('?url=')) { return { url: link, type: 'image' }; @@ -93,16 +96,19 @@ export const getLinkMediaInfo = memoize( } try { - mime = extName(url.pathname.toLowerCase().replace('/', ''))[0]?.mime; - if (mime) { - if (mime.startsWith('image')) { - type = mime === 'image/gif' ? 'gif' : 'image'; - } else if (mime.startsWith('video')) { - type = 'video'; - } else if (mime.startsWith('audio')) { - type = 'audio'; - } + // Extract file extension + const pathParts = url.pathname.toLowerCase().split('.'); + const extension = pathParts.length > 1 ? pathParts[pathParts.length - 1] : ''; + + // Only classify as media if we explicitly know the extension + if (KNOWN_IMAGE_EXTENSIONS.includes(extension)) { + type = extension === 'gif' ? 'gif' : 'image'; + } else if (KNOWN_VIDEO_EXTENSIONS.includes(extension)) { + type = 'video'; + } else if (KNOWN_AUDIO_EXTENSIONS.includes(extension)) { + type = 'audio'; } + // Unknown extensions remain as 'webpage' if (!url.pathname.includes('.')) { type = 'webpage'; diff --git a/src/modules.d.ts b/src/modules.d.ts index 9d2f2b5d..9147c7a3 100644 --- a/src/modules.d.ts +++ b/src/modules.d.ts @@ -3,8 +3,6 @@ declare module '*.module.css' { export default classes; } -declare module 'ext-name'; - declare module 'lodash'; declare module 'react-draggable'; From b3bcd5896c8880fdcb99423baaf3bab9be705921 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 12:55:55 +0200 Subject: [PATCH 2/8] feat(markdown): render plebchan links as internal links, so the user doesn't have to leave the app --- src/components/markdown/markdown.tsx | 62 ++++++++++++-- src/lib/utils/url-utils.ts | 117 +++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 7 deletions(-) diff --git a/src/components/markdown/markdown.tsx b/src/components/markdown/markdown.tsx index 92f97d2e..ecf424df 100644 --- a/src/components/markdown/markdown.tsx +++ b/src/components/markdown/markdown.tsx @@ -11,8 +11,9 @@ import { isCatalogView } from '../../lib/utils/view-utils'; import useIsMobile from '../../hooks/use-is-mobile'; import CommentMedia from '../comment-media'; import styles from './markdown.module.css'; -import { useLocation, useParams } from 'react-router-dom'; +import { Link, useLocation, useParams } from 'react-router-dom'; import { canEmbed } from '../embed'; +import { isPlebchanLink, transformPlebchanLinkToInternal, preprocessPlebchanPatterns } from '../../lib/utils/url-utils'; interface ContentLinkEmbedProps { children: any; @@ -153,6 +154,52 @@ interface MarkdownProps { title?: string; } +const renderAnchorLink = (children: React.ReactNode, href: string) => { + if (!href) { + return {children}; + } + + // Check if this is a valid plebchan link that should be handled internally + if (isPlebchanLink(href)) { + const internalPath = transformPlebchanLinkToInternal(href); + if (internalPath) { + // Check if the link text should be replaced with the internal path + let shouldReplaceText = false; + + if (typeof children === 'string') { + shouldReplaceText = children === href || children.trim() === href.trim(); + } else if (Array.isArray(children) && children.length === 1 && typeof children[0] === 'string') { + shouldReplaceText = children[0] === href || children[0].trim() === href.trim(); + } + + // For display purposes, remove leading slash from paths like "/p/something" + let displayText: React.ReactNode = children; + if (shouldReplaceText && internalPath.startsWith('/p/')) { + displayText = internalPath.substring(1); // Remove leading slash + } else if (shouldReplaceText) { + displayText = internalPath; + } + + return {displayText}; + } else { + console.warn('Failed to transform plebchan link to internal path:', href); + return {children}; + } + } + + // Handle hash routes and internal patterns (including routes that start with /#/) + if (href.startsWith('#/') || href.startsWith('/#/') || href.startsWith('/p/') || href.match(/^\/p\/[^/]+(\/c\/[^/]+)?$/)) { + return {children}; + } + + // External links + return ( + + {children} + + ); +}; + const Markdown = ({ content, title }: MarkdownProps) => { const remarkPlugins: any[] = [[supersub]]; @@ -179,6 +226,9 @@ const Markdown = ({ content, title }: MarkdownProps) => { const isInCatalogView = isCatalogView(useLocation().pathname, useParams()); + // Preprocess content to convert plain text plebchan patterns to markdown links + const processedContent = preprocessPlebchanPatterns(content || ''); + return ( {isInCatalogView && title && ( @@ -188,7 +238,7 @@ const Markdown = ({ content, title }: MarkdownProps) => { )} { console.debug('Invalid URL:', href); } - return ( - - {children} - - ); + return renderAnchorLink(children, href); } + + return renderAnchorLink(children, href || ''); }, } as ExtendedComponents } diff --git a/src/lib/utils/url-utils.ts b/src/lib/utils/url-utils.ts index b51b7e31..9edc727f 100644 --- a/src/lib/utils/url-utils.ts +++ b/src/lib/utils/url-utils.ts @@ -21,3 +21,120 @@ export const copyShareLinkToClipboard = async (subplebbitAddress: string, cid: s const shareLink = `https://pleb.bz/p/${subplebbitAddress}/c/${cid}?redirect=plebchan.app`; await copyToClipboard(shareLink); }; + +const PLEBCHAN_HOSTNAMES = ['pleb.bz', 'plebchan.app', 'plebchan.eth.limo', 'plebchan.eth.link', 'plebchan.eth.sucks', 'plebchan.netlify.app']; + +// Check if a URL is a valid plebchan link that should be handled internally +export const isPlebchanLink = (url: string): boolean => { + try { + const parsedUrl = new URL(url); + const hostname = parsedUrl.hostname.replace(/^www\./, ''); + + if (!PLEBCHAN_HOSTNAMES.includes(hostname)) { + return false; + } + + // Check both pathname and hash for the route pattern + let routePath = parsedUrl.pathname; + + // If there's a hash that starts with #/, use that as the route path + if (parsedUrl.hash && parsedUrl.hash.startsWith('#/')) { + routePath = parsedUrl.hash.substring(1); // Remove the # to get the path + } + + // For pleb.bz, only support the exact sharelink format + if (hostname === 'pleb.bz') { + // Must match exactly: /p/{subplebbitAddress}/c/{cid} + // Allow redirect parameter since these are still valid internal links + return /^\/p\/[^/]+\/c\/[^/]+$/.test(routePath); + } + + // For other plebchan hostnames, support: + // - /p/{subplebbitAddress} + // - /p/{subplebbitAddress}/c/{commentCid} + return /^\/p\/[^/]+(\/c\/[^/]+)?$/.test(routePath); + } catch { + return false; + } +}; + +// Transform a valid plebchan URL to an internal route +export const transformPlebchanLinkToInternal = (url: string): string | null => { + if (!isPlebchanLink(url)) { + return null; + } + + try { + const parsedUrl = new URL(url); + + // Check if this is a hash-based route + if (parsedUrl.hash && parsedUrl.hash.startsWith('#/')) { + // Extract the route from the hash, preserving any query params within the hash + const hashPath = parsedUrl.hash.substring(1); // Remove the # + return hashPath; + } + + // For regular pathname-based routes, remove redirect parameter from query string + const searchParams = new URLSearchParams(parsedUrl.search); + searchParams.delete('redirect'); // Remove redirect parameter for cleaner internal links + + const cleanSearch = searchParams.toString(); + const searchString = cleanSearch ? `?${cleanSearch}` : ''; + + return parsedUrl.pathname + searchString + parsedUrl.hash; + } catch { + return null; + } +}; + +// Check if a string is a valid IPNS public key (52 chars starting with 12D3KooW) +const isValidIPNSKey = (str: string): boolean => { + return str.length === 52 && str.startsWith('12D3KooW'); +}; + +// Check if a string is a valid domain (contains a dot) +const isValidDomain = (str: string): boolean => { + return str.includes('.') && str.split('.').length >= 2 && str.split('.').every((part) => part.length > 0); +}; + +// Check if a plain text pattern is a valid plebchan subplebbit reference +export const isValidSubplebbitPattern = (pattern: string): boolean => { + // Must start with "p/" + if (!pattern.startsWith('p/')) { + return false; + } + + const pathPart = pattern.substring(2); // Remove "p/" + + // Check if it's a post pattern: subplebbitAddress/c/cid + const postMatch = pathPart.match(/^([^/]+)\/c\/([^/]+)$/); + if (postMatch) { + const [, subplebbitAddress, cid] = postMatch; + // CID should be at least 10 characters (minimum reasonable CID length) + return (isValidDomain(subplebbitAddress) || isValidIPNSKey(subplebbitAddress)) && cid.length >= 10; + } + + // Check if it's just a subplebbit pattern: subplebbitAddress + return isValidDomain(pathPart) || isValidIPNSKey(pathPart); +}; + +// Preprocess content to convert plain text plebchan patterns to markdown links +export const preprocessPlebchanPatterns = (content: string): string => { + // Pattern to match "p/something" or "p/something/c/something" + // Negative lookbehind prevents matching patterns that are already part of URLs + const pattern = /(? { + // Remove any trailing punctuation from the captured path + const cleanPath = capturedPath.replace(/[.,:;!?]+$/, ''); + const fullPattern = `p/${cleanPath}`; + + if (isValidSubplebbitPattern(fullPattern)) { + // Preserve trailing punctuation outside the link + const trailingPunctuation = match.slice(fullPattern.length); + return `[${fullPattern}](/${fullPattern})${trailingPunctuation}`; + } + + return match; + }); +}; From bb0fcb9c1a97d2dac7ed08544bb406d3d99f0bba Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 12:59:06 +0200 Subject: [PATCH 3/8] fix(challenge modal): on mobile the modal was incorrectly positioned on top left --- src/components/challenge-modal/challenge-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/challenge-modal/challenge-modal.tsx b/src/components/challenge-modal/challenge-modal.tsx index 905f31c0..afe11fd7 100644 --- a/src/components/challenge-modal/challenge-modal.tsx +++ b/src/components/challenge-modal/challenge-modal.tsx @@ -96,8 +96,8 @@ const Challenge = ({ challenge, closeModal }: ChallengeProps) => { className={styles.container} ref={nodeRef} style={{ - x: isMobile ? 0 : x, - y: isMobile ? 0 : y, + x: isMobile ? window.innerWidth / 2 - 150 : x, + y: isMobile ? window.innerHeight / 2 - 200 : y, touchAction: 'none', }} > From bc805337400c5233e829c823606d8a0401bedf94 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 14:54:11 +0200 Subject: [PATCH 4/8] fix(board header): subplebbit address could be too long --- src/components/board-header/board-header.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/board-header/board-header.tsx b/src/components/board-header/board-header.tsx index 1aa0150d..c9372328 100644 --- a/src/components/board-header/board-header.tsx +++ b/src/components/board-header/board-header.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useLocation, useParams } from 'react-router-dom'; import { useAccountComment } from '@plebbit/plebbit-react-hooks'; +import Plebbit from '@plebbit/plebbit-js'; import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits'; import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils'; import styles from './board-header.module.css'; @@ -48,7 +49,12 @@ const BoardHeader = () => { )}
- {title || (shortAddress ? (shortAddress.endsWith('.eth') || shortAddress.endsWith('.sol') ? shortAddress.slice(0, -4) : shortAddress) : subplebbitAddress)} + {title || + (shortAddress + ? shortAddress.endsWith('.eth') || shortAddress.endsWith('.sol') + ? shortAddress.slice(0, -4) + : shortAddress + : Plebbit.getShortAddress(subplebbitAddress))} {(isOffline || isOnlineStatusLoading) && !isInAllView && !isInSubscriptionsView && !isInModView && ( From 20c601ac18b67cc1abd8c36b605aa7fc8c3dbbd8 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 15:00:46 +0200 Subject: [PATCH 5/8] Update release-body.js --- scripts/release-body.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release-body.js b/scripts/release-body.js index ee6507dd..59c28611 100644 --- a/scripts/release-body.js +++ b/scripts/release-body.js @@ -17,7 +17,7 @@ let releaseChangelog = // format releaseChangelog = releaseChangelog.trim().replace(/\n\n+/g, '\n\n') -const releaseBody = `This version fixes a bug where the android app would launch in fullscreen mode. +const releaseBody = `This version adds minor features and fixes some bugs. - Web app: https://plebchan.app - Decentralized web app: https://plebchan.eth (only works on [Brave Browser](https://brave.com/) or via [IPFS Companion](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites)) From 53e04823df833bb376770892d1f51b0ecbdaa8c8 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 16:36:16 +0200 Subject: [PATCH 6/8] feat(board): add search bar to board page, redirect to catalog view --- .../board-buttons/board-buttons.module.css | 21 +++++ .../board-buttons/board-buttons.tsx | 85 +++++++++++++++---- 2 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/components/board-buttons/board-buttons.module.css b/src/components/board-buttons/board-buttons.module.css index 59d754e4..f46b0f28 100644 --- a/src/components/board-buttons/board-buttons.module.css +++ b/src/components/board-buttons/board-buttons.module.css @@ -88,6 +88,27 @@ margin-bottom: 15px; } +.searchOPsInput { + height: 16px; + line-height: 16px; + margin-left: 2px; + padding: 0 2px; + width: 120px; + margin: 0px; + margin-right: 4px; + padding: 0 2px; + border: var(--post-form-field-input-border, revert); + background-color: var(--post-form-field-input-background-color, revert); + color: var(--post-form-field-input-color); + outline: none; + font-family: arial, helvetica, sans-serif; + font-size: 10pt; +} + +.searchOPsInput:focus { + border: var(--post-form-field-input-focus-border, revert); +} + @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 61ce220c..0e482dc7 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -3,7 +3,7 @@ import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useAccountComment, useSubscribe } from '@plebbit/plebbit-react-hooks'; import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits'; import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages'; -import { isAllView, isCatalogView, isDescriptionView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; +import { isAllView, isCatalogView, isDescriptionView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; import useCatalogStyleStore from '../../stores/use-catalog-style-store'; import useFeedResetStore from '../../stores/use-feed-reset-store'; @@ -22,10 +22,11 @@ interface BoardButtonsProps { isInAllView?: boolean; isInCatalogView?: boolean; isInSubscriptionsView?: boolean; + isInModView?: boolean; isTopbar?: boolean; } -const CatalogButton = ({ address, isInAllView, isInSubscriptionsView }: BoardButtonsProps) => { +const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => { const { t } = useTranslation(); const params = useParams(); @@ -36,6 +37,9 @@ const CatalogButton = ({ address, isInAllView, isInSubscriptionsView }: BoardBut } else if (isInSubscriptionsView) { if (params?.timeFilterName) return `/p/subscriptions/catalog/${params.timeFilterName}`; return `/p/subscriptions/catalog`; + } else if (isInModView) { + if (params?.timeFilterName) return `/p/mod/catalog/${params.timeFilterName}`; + return `/p/mod/catalog`; } return `/p/${address}/catalog`; }; @@ -58,7 +62,7 @@ const SubscribeButton = ({ address }: BoardButtonsProps) => { ); }; -const ReturnButton = ({ address, isInAllView, isInSubscriptionsView }: BoardButtonsProps) => { +const ReturnButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => { const { t } = useTranslation(); const params = useParams(); @@ -69,6 +73,9 @@ const ReturnButton = ({ address, isInAllView, isInSubscriptionsView }: BoardButt } else if (isInSubscriptionsView) { if (params?.timeFilterName) return `/p/subscriptions/${params.timeFilterName}`; return `/p/subscriptions`; + } else if (isInModView) { + if (params?.timeFilterName) return `/p/mod/${params.timeFilterName}`; + return `/p/mod`; } return `/p/${address}`; }; @@ -190,7 +197,7 @@ const ShowOPCommentOption = () => { ); }; -export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView, isTopbar = false }: BoardButtonsProps) => { +export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView, isInModView, isTopbar = false }: BoardButtonsProps) => { const { t } = useTranslation(); const navigate = useNavigate(); const { timeFilterName, timeFilterNames } = useTimeFilter(); @@ -205,6 +212,10 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView ? isInCatalogView ? `/p/subscriptions/catalog/${timeFilterName}` : `/p/subscriptions/${timeFilterName}` + : isInModView + ? isInCatalogView + ? `/p/mod/catalog/${timeFilterName}` + : `/p/mod/${timeFilterName}` : null; link && navigate(link); }; @@ -242,6 +253,7 @@ export const MobileBoardButtons = () => { const isInPendingPostPage = isPendingPostView(location.pathname, params); const isInPostView = isPostPageView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); + const isInModView = isModView(location.pathname); const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any }); const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress; @@ -252,8 +264,8 @@ export const MobileBoardButtons = () => {
{isInPostView || isInPendingPostPage ? ( <> - - + +
@@ -263,11 +275,11 @@ export const MobileBoardButtons = () => { ) : ( <> {isInCatalogView ? ( - + ) : ( - + )} - {!(isInAllView || isInSubscriptionsView) && } + {!(isInAllView || isInSubscriptionsView || isInModView) && } {isInCatalogView && searchText ? ( @@ -338,6 +350,7 @@ export const DesktopBoardButtons = () => { const isInPendingPostPage = isPendingPostView(location.pathname, params); const isInPostView = isPostPageView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); + const isInModView = isModView(location.pathname); const { filteredCount, searchText } = useCatalogFiltersStore(); @@ -348,9 +361,9 @@ export const DesktopBoardButtons = () => { {isInPostView || isInPendingPostPage ? ( <> [ - + ] [ - ] [ + ] [ ] [ ] @@ -363,12 +376,13 @@ export const DesktopBoardButtons = () => { {isInCatalogView ? ( <> [ - ]{' '} + ]{' '} ) : ( <> + [ - ]{' '} + ]{' '} )} [] @@ -394,10 +408,10 @@ export const DesktopBoardButtons = () => { )} - {(isInAllView || isInSubscriptionsView) && ( - + {(isInAllView || isInSubscriptionsView || isInModView) && ( + )} - {!(isInAllView || isInSubscriptionsView) && ( + {!(isInAllView || isInSubscriptionsView || isInModView) && ( <> [ ] @@ -415,3 +429,42 @@ export const DesktopBoardButtons = () => { ); }; + +const SearchOPsBar = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const params = useParams(); + const location = useLocation(); + const isInAllView = isAllView(location.pathname); + const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); + const isInModView = isModView(location.pathname); + + const handleSearch = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + const searchQuery = (event.target as HTMLInputElement).value.trim(); + if (searchQuery) { + let catalogUrl = ''; + + if (isInAllView) { + catalogUrl = params?.timeFilterName + ? `/p/all/catalog/${params.timeFilterName}?q=${encodeURIComponent(searchQuery)}` + : `/p/all/catalog?q=${encodeURIComponent(searchQuery)}`; + } else if (isInSubscriptionsView) { + catalogUrl = params?.timeFilterName + ? `/p/subscriptions/catalog/${params.timeFilterName}?q=${encodeURIComponent(searchQuery)}` + : `/p/subscriptions/catalog?q=${encodeURIComponent(searchQuery)}`; + } else if (isInModView) { + catalogUrl = params?.timeFilterName + ? `/p/mod/catalog/${params.timeFilterName}?q=${encodeURIComponent(searchQuery)}` + : `/p/mod/catalog?q=${encodeURIComponent(searchQuery)}`; + } else { + catalogUrl = `/p/${params?.subplebbitAddress}/catalog?q=${encodeURIComponent(searchQuery)}`; + } + + navigate(catalogUrl); + } + } + }; + + return ; +}; From 6075b2ebf7b7bcfc6e3bcb57dec4bb53988f658f Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 16:36:40 +0200 Subject: [PATCH 7/8] fix(catalog search): use query parameter so users can share searches and link to them --- .../catalog-search/catalog-search.module.css | 14 +++---- .../catalog-search/catalog-search.tsx | 42 +++++++++++++++++-- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/components/catalog-search/catalog-search.module.css b/src/components/catalog-search/catalog-search.module.css index 6c492561..51206fa7 100644 --- a/src/components/catalog-search/catalog-search.module.css +++ b/src/components/catalog-search/catalog-search.module.css @@ -1,11 +1,11 @@ .button { text-transform: capitalize; - color: var(--button-desktop-text-color); - text-decoration: var(--button-text-decoration); + color: var(--button-desktop-text-color, revert); + text-decoration: var(--button-text-decoration, revert); } .button:hover { - color: var(--button-desktop-text-color-hover); + color: var(--button-desktop-text-color-hover, revert); cursor: pointer; } @@ -22,23 +22,23 @@ margin: 0px 5px; padding: 1px; outline: none; - border: 1px solid #AAA; + border: var(--post-form-field-input-border, revert); font-size: 11px; } .searchContainer input:focus { - border: var(--post-form-field-input-focus-border); + border: var(--post-form-field-input-focus-border, revert); } .closeSearch { cursor: pointer; border: none; white-space: nowrap; - color: var(--button-desktop-text-color); + color: var(--button-desktop-text-color, revert); } .closeSearch:hover { - color: var(--button-desktop-text-color-hover); + color: var(--button-desktop-text-color-hover, revert); } @media (max-width: 640px) { diff --git a/src/components/catalog-search/catalog-search.tsx b/src/components/catalog-search/catalog-search.tsx index 1c25c08b..3be3ad64 100644 --- a/src/components/catalog-search/catalog-search.tsx +++ b/src/components/catalog-search/catalog-search.tsx @@ -1,5 +1,6 @@ import { useEffect, useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { useLocation, useNavigate } from 'react-router-dom'; import styles from './catalog-search.module.css'; import useIsMobile from '../../hooks/use-is-mobile'; import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; @@ -7,21 +8,52 @@ import _ from 'lodash'; const CatalogSearch = () => { const { t } = useTranslation(); + const location = useLocation(); + const navigate = useNavigate(); const [openSearch, setOpenSearch] = useState(false); const [inputValue, setInputValue] = useState(''); const { setSearchFilter, clearSearchFilter } = useCatalogFiltersStore(); - // Create a debounced version of setSearchFilter + // Extract query parameter from URL + useEffect(() => { + const urlParams = new URLSearchParams(location.search); + const queryParam = urlParams.get('q'); + if (queryParam) { + setInputValue(queryParam); + setSearchFilter(queryParam); + setOpenSearch(true); + } + }, [location.search, setSearchFilter]); + + // Update URL when search changes + const updateURL = useCallback( + (searchText: string) => { + const urlParams = new URLSearchParams(location.search); + if (searchText.trim()) { + urlParams.set('q', searchText); + } else { + urlParams.delete('q'); + } + const newSearch = urlParams.toString(); + const newPath = location.pathname + (newSearch ? `?${newSearch}` : ''); + navigate(newPath, { replace: true }); + }, + [location.pathname, location.search, navigate], + ); + + // Create a debounced version of setSearchFilter and URL update // eslint-disable-next-line react-hooks/exhaustive-deps const debouncedSetSearchFilter = useCallback( _.debounce((text: string) => { if (text.trim()) { setSearchFilter(text); + updateURL(text); } else { clearSearchFilter(); + updateURL(''); } }, 300), - [setSearchFilter, clearSearchFilter], + [setSearchFilter, clearSearchFilter, updateURL], ); const handleToggleSearch = useCallback(() => { @@ -30,14 +62,16 @@ const CatalogSearch = () => { if (openSearch) { setInputValue(''); clearSearchFilter(); + updateURL(''); } - }, [openSearch, clearSearchFilter]); + }, [openSearch, clearSearchFilter, updateURL]); const handleCloseSearch = useCallback(() => { setOpenSearch(false); setInputValue(''); clearSearchFilter(); - }, [clearSearchFilter]); + updateURL(''); + }, [clearSearchFilter, updateURL]); useEffect(() => { if (openSearch) { From 339a9bc18a6ca4414a9b80f85b5d12799f68cf94 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Wed, 4 Jun 2025 16:37:17 +0200 Subject: [PATCH 8/8] fix(mod multisub): mod page was missing post form --- 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 | 4 ++- 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/app.tsx | 9 +++--- src/components/board-header/board-header.tsx | 11 ++++++- src/components/post-form/post-form.tsx | 30 +++++++++++++++----- src/views/catalog/catalog.tsx | 8 ++++-- 39 files changed, 114 insertions(+), 50 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 3afae8d0..497b4e74 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -294,5 +294,6 @@ "search_results_for": "نتائج البحث عن", "help": "مساعدة", "filter_and_highlights_help": "مساعدة الفلاتر والإبرازات", - "filter_and_highlights": "التصفية والتسليط الضوء" + "filter_and_highlights": "التصفية والتسليط الضوء", + "boards_you_moderate": "المنتديات التي تقوم بالإشراف عليها" } \ No newline at end of file diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 9e550c59..309fa637 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -294,5 +294,6 @@ "search_results_for": "অনুসন্ধান ফলাফল", "help": "সাহায্য", "filter_and_highlights_help": "ফিল্টার এবং হাইলাইট সাহায্য", - "filter_and_highlights": "ফিল্টার এবং হাইলাইট" + "filter_and_highlights": "ফিল্টার এবং হাইলাইট", + "boards_you_moderate": "আপনি যে বোর্ডগুলি পরিচালনা করেন" } \ No newline at end of file diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index f804abbf..93873900 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -294,5 +294,6 @@ "search_results_for": "Výsledky hledání pro", "help": "Pomoc", "filter_and_highlights_help": "Pomoc s filtry a zvýrazněním", - "filter_and_highlights": "Filtry a zvýraznění" + "filter_and_highlights": "Filtry a zvýraznění", + "boards_you_moderate": "Fóra, která moderujete" } \ No newline at end of file diff --git a/public/translations/da/default.json b/public/translations/da/default.json index c0a7dbbe..e70236d7 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -294,5 +294,6 @@ "search_results_for": "Søgeresultater for", "help": "Hjælp", "filter_and_highlights_help": "Filtre og fremhævelser Hjælp", - "filter_and_highlights": "Filtre og fremhævninger" + "filter_and_highlights": "Filtre og fremhævninger", + "boards_you_moderate": "Boards du modererer" } \ No newline at end of file diff --git a/public/translations/de/default.json b/public/translations/de/default.json index ad6bc063..f660f65c 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -294,5 +294,6 @@ "search_results_for": "Suchergebnisse für", "help": "Hilfe", "filter_and_highlights_help": "Filter- und Markierungs-Hilfe", - "filter_and_highlights": "Filter und Hervorhebungen" + "filter_and_highlights": "Filter und Hervorhebungen", + "boards_you_moderate": "Boards, die Sie moderieren" } \ No newline at end of file diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 40113a00..f3a5c50c 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -294,5 +294,6 @@ "search_results_for": "Αποτελέσματα αναζήτησης για", "help": "Βοήθεια", "filter_and_highlights_help": "Βοήθεια φίλτρων και επισήμανσης", - "filter_and_highlights": "Φίλτρα και Επισημάνσεις" + "filter_and_highlights": "Φίλτρα και Επισημάνσεις", + "boards_you_moderate": "Boards που διαχειρίζεστε" } \ No newline at end of file diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 914a4943..9febc6f1 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -141,6 +141,7 @@ "user_deleted_this_post": "User deleted this post.", "reason": "reason", "search": "search", + "search_ops_placeholder": "Search OPs...", "ban_user_for": "Ban user for <1> days", "optional": "optional", "close_thread": "close thread", @@ -294,5 +295,6 @@ "search_results_for": "Search results for", "help": "Help", "filter_and_highlights_help": "Filters & Highlights Help", - "filter_and_highlights": "Filters & Highlights" + "filter_and_highlights": "Filters & Highlights", + "boards_you_moderate": "boards you moderate" } \ No newline at end of file diff --git a/public/translations/es/default.json b/public/translations/es/default.json index fd006523..ab25f0fc 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -294,5 +294,6 @@ "search_results_for": "Resultados de búsqueda para", "help": "Ayuda", "filter_and_highlights_help": "Ayuda de filtros y resaltes", - "filter_and_highlights": "Filtros y resaltados" + "filter_and_highlights": "Filtros y resaltados", + "boards_you_moderate": "tableros que moderas" } \ No newline at end of file diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index 108ada90..dd2e4efa 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -294,5 +294,6 @@ "search_results_for": "نتایج جستجو برای", "help": "کمک", "filter_and_highlights_help": "کمک فیلترها و برجسته‌ها", - "filter_and_highlights": "فیلترها و برجسته‌ها" + "filter_and_highlights": "فیلترها و برجسته‌ها", + "boards_you_moderate": "تابلوهایی که مدیریت می‌کنید" } \ No newline at end of file diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index febffbfb..ec8f215c 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -294,5 +294,6 @@ "search_results_for": "Hakutulokset hakusanalle", "help": "Apua", "filter_and_highlights_help": "Suodattimet ja korostukset -apu", - "filter_and_highlights": "Suodattimet ja korostukset" + "filter_and_highlights": "Suodattimet ja korostukset", + "boards_you_moderate": "lautakunnat, joita valvot" } \ No newline at end of file diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 83f0572c..a8fafc11 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -294,5 +294,6 @@ "search_results_for": "Mga resulta ng paghahanap para sa", "help": "Tulong", "filter_and_highlights_help": "Tulong sa mga filter at highlight", - "filter_and_highlights": "Mga filter at highlight" + "filter_and_highlights": "Mga filter at highlight", + "boards_you_moderate": "mga board na iyong minomoderate" } \ No newline at end of file diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index 3de24e5d..eed42fe2 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -294,5 +294,6 @@ "search_results_for": "Résultats de recherche pour", "help": "Aide", "filter_and_highlights_help": "Aide sur les filtres et surlignages", - "filter_and_highlights": "Filtres et surlignages" + "filter_and_highlights": "Filtres et surlignages", + "boards_you_moderate": "boards que vous modérez" } \ No newline at end of file diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 100cad37..b9bb6c3d 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -294,5 +294,6 @@ "search_results_for": "תוצאות חיפוש עבור", "help": "עזרה", "filter_and_highlights_help": "עזרה במסננים והדגשות", - "filter_and_highlights": "מסננים והדגשות" + "filter_and_highlights": "מסננים והדגשות", + "boards_you_moderate": "לוחות שאתה מפקח עליהם" } \ No newline at end of file diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index b89d3be3..a1c42d3f 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -294,5 +294,6 @@ "search_results_for": "के लिए खोज परिणाम", "help": "मदद", "filter_and_highlights_help": "फ़िल्टर और हाइलाइट्स सहायता", - "filter_and_highlights": "फ़िल्टर और हाइलाइट्स" + "filter_and_highlights": "फ़िल्टर और हाइलाइट्स", + "boards_you_moderate": "बोर्ड जो आप मॉडरेट करते हैं" } \ No newline at end of file diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index 1200f8ba..666daaa6 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -294,5 +294,6 @@ "search_results_for": "Keresési eredmények", "help": "Segítség", "filter_and_highlights_help": "Szűrők és kiemelések súgó", - "filter_and_highlights": "Szűrők és kiemelések" + "filter_and_highlights": "Szűrők és kiemelések", + "boards_you_moderate": "boardok, amelyeket moderálsz" } \ No newline at end of file diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 0104b274..5eef3d29 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -294,5 +294,6 @@ "search_results_for": "Hasil pencarian untuk", "help": "Bantuan", "filter_and_highlights_help": "Bantuan Filter dan Sorotan", - "filter_and_highlights": "Filter dan Sorotan" + "filter_and_highlights": "Filter dan Sorotan", + "boards_you_moderate": "board yang Anda moderasi" } \ No newline at end of file diff --git a/public/translations/it/default.json b/public/translations/it/default.json index adccb163..8b2baf25 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -294,5 +294,6 @@ "search_results_for": "Risultati di ricerca per", "help": "Aiuto", "filter_and_highlights_help": "Aiuto su Filtri e Evidenziazioni", - "filter_and_highlights": "Filtri e Evidenziazioni" + "filter_and_highlights": "Filtri e Evidenziazioni", + "boards_you_moderate": "board che moderi" } \ No newline at end of file diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index cde06d87..e6fb2884 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -294,5 +294,6 @@ "search_results_for": "検索結果", "help": "ヘルプ", "filter_and_highlights_help": "フィルターとハイライトのヘルプ", - "filter_and_highlights": "フィルターとハイライト" + "filter_and_highlights": "フィルターとハイライト", + "boards_you_moderate": "あなたが管理するボード" } \ No newline at end of file diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index ed9f4f09..0fbb21f6 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -294,5 +294,6 @@ "search_results_for": "검색 결과", "help": "도움말", "filter_and_highlights_help": "필터 및 하이라이트 도움말", - "filter_and_highlights": "필터 및 하이라이트" + "filter_and_highlights": "필터 및 하이라이트", + "boards_you_moderate": "당신이 관리하는 게시판" } \ No newline at end of file diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 73094525..b42e1862 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -294,5 +294,6 @@ "search_results_for": "सर्च परिणाम", "help": "मदत", "filter_and_highlights_help": "फिल्टर आणि हायलाइट्स सहाय्य", - "filter_and_highlights": "फिल्टर आणि हायलाइट्स" + "filter_and_highlights": "फिल्टर आणि हायलाइट्स", + "boards_you_moderate": "तुम्ही ज्यांचे नियंत्रण करता अशा बोर्ड्स" } \ No newline at end of file diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 26ada5c2..d0d0f3cb 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -294,5 +294,6 @@ "search_results_for": "Zoekresultaten voor", "help": "Help", "filter_and_highlights_help": "Filters en Markeringen Help", - "filter_and_highlights": "Filters en markeringen" + "filter_and_highlights": "Filters en markeringen", + "boards_you_moderate": "boards die je beheert" } \ No newline at end of file diff --git a/public/translations/no/default.json b/public/translations/no/default.json index 49731889..2fbc0794 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -294,5 +294,6 @@ "search_results_for": "Søkeresultater for", "help": "Hjelp", "filter_and_highlights_help": "Filtre og fremhevinger Hjelp", - "filter_and_highlights": "Filtre og uthevinger" + "filter_and_highlights": "Filtre og uthevinger", + "boards_you_moderate": "boards du modererer" } \ No newline at end of file diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index d36af04c..ec963255 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -294,5 +294,6 @@ "search_results_for": "Wyniki wyszukiwania dla", "help": "Pomoc", "filter_and_highlights_help": "Pomoc dotycząca filtrów i wyróżnień", - "filter_and_highlights": "Filtry i wyróżnienia" + "filter_and_highlights": "Filtry i wyróżnienia", + "boards_you_moderate": "boards, które moderujesz" } \ No newline at end of file diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 1c654fb6..230583d8 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -294,5 +294,6 @@ "search_results_for": "Resultados de pesquisa para", "help": "Ajuda", "filter_and_highlights_help": "Ajuda de Filtros e Realces", - "filter_and_highlights": "Filtros e Destaques" + "filter_and_highlights": "Filtros e Destaques", + "boards_you_moderate": "boards que você modera" } \ No newline at end of file diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index e1b0574b..d4add305 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -294,5 +294,6 @@ "search_results_for": "Rezultatele căutării pentru", "help": "Ajutor", "filter_and_highlights_help": "Ajutor pentru filtre și evidențieri", - "filter_and_highlights": "Filtre și evidențieri" + "filter_and_highlights": "Filtre și evidențieri", + "boards_you_moderate": "boards pe care le moderezi" } \ No newline at end of file diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index ea059ba3..b70e9a17 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -294,5 +294,6 @@ "search_results_for": "Результаты поиска для", "help": "Помощь", "filter_and_highlights_help": "Помощь с фильтрами и выделениями", - "filter_and_highlights": "Фильтры и выделения" + "filter_and_highlights": "Фильтры и выделения", + "boards_you_moderate": "boards, которые вы модерируете" } \ No newline at end of file diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index 8c02007b..b33080ab 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -294,5 +294,6 @@ "search_results_for": "Rezultatet e kërkimit për", "help": "Ndihmë", "filter_and_highlights_help": "Ndihma për Filtro & Thekso", - "filter_and_highlights": "Filtra dhe thekson" + "filter_and_highlights": "Filtra dhe thekson", + "boards_you_moderate": "boards që i moderoni" } \ No newline at end of file diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index bb113cf7..5a69c3d3 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -294,5 +294,6 @@ "search_results_for": "Sökresultat för", "help": "Hjälp", "filter_and_highlights_help": "Filter och markeringar Hjälp", - "filter_and_highlights": "Filter och markeringar" + "filter_and_highlights": "Filter och markeringar", + "boards_you_moderate": "boards du modererar" } \ No newline at end of file diff --git a/public/translations/te/default.json b/public/translations/te/default.json index 668cba68..11f9e583 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -294,5 +294,6 @@ "search_results_for": "శోధన ఫలితాలు", "help": "సహాయం", "filter_and_highlights_help": "ఫిల్టర్లు మరియు హైలైట్ సహాయం", - "filter_and_highlights": "ఫిల్టర్లు మరియు హైలైట్స్" + "filter_and_highlights": "ఫిల్టర్లు మరియు హైలైట్స్", + "boards_you_moderate": "మీరు మోడరేట్ చేసే బోర్డులు" } \ No newline at end of file diff --git a/public/translations/th/default.json b/public/translations/th/default.json index c448d395..803be4e7 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -294,5 +294,6 @@ "search_results_for": "ผลการค้นหาสำหรับ", "help": "ช่วยเหลือ", "filter_and_highlights_help": "ตัวกรองและการเน้นความช่วยเหลือ", - "filter_and_highlights": "ตัวกรองและไฮไลท์" + "filter_and_highlights": "ตัวกรองและไฮไลท์", + "boards_you_moderate": "บอร์ดที่คุณดูแล" } \ No newline at end of file diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 3bee0dc2..b197ac77 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -294,5 +294,6 @@ "search_results_for": "Arama sonuçları için", "help": "Yardım", "filter_and_highlights_help": "Filtreler ve Vurgular Yardımı", - "filter_and_highlights": "Filtreler ve Vurgular" + "filter_and_highlights": "Filtreler ve Vurgular", + "boards_you_moderate": "modere ettiğiniz panolar" } \ No newline at end of file diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index 5399a654..7da2f5c7 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -294,5 +294,6 @@ "search_results_for": "Результати пошуку для", "help": "Допомога", "filter_and_highlights_help": "Допомога з фільтрами та підсвічуваннями", - "filter_and_highlights": "Фільтри та виділення" + "filter_and_highlights": "Фільтри та виділення", + "boards_you_moderate": "boards, які ви модеруватимете" } \ No newline at end of file diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 1475621b..54f20b41 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -294,5 +294,6 @@ "search_results_for": "تلاش کے نتائج", "help": "مدد", "filter_and_highlights_help": "فلٹرز اور ہائی لائٹس کی مدد", - "filter_and_highlights": "فلٹرز اور ہائلाइटس" + "filter_and_highlights": "فلٹرز اور ہائلाइटس", + "boards_you_moderate": "وہ بورڈز جن کی آپ نگرانی کرتے ہیں" } \ No newline at end of file diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index 492d50fa..52b4e0a1 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -294,5 +294,6 @@ "search_results_for": "Kết quả tìm kiếm cho", "help": "Trợ giúp", "filter_and_highlights_help": "Trợ giúp bộ lọc và đánh dấu", - "filter_and_highlights": "Bộ lọc và Nổi bật" + "filter_and_highlights": "Bộ lọc và Nổi bật", + "boards_you_moderate": "boards bạn điều hành" } \ No newline at end of file diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 9b8591d3..d776cf13 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -294,5 +294,6 @@ "search_results_for": "搜索结果", "help": "帮助", "filter_and_highlights_help": "过滤器和高亮帮助", - "filter_and_highlights": "筛选器与高亮" + "filter_and_highlights": "筛选器与高亮", + "boards_you_moderate": "您管理的板块" } \ No newline at end of file diff --git a/src/app.tsx b/src/app.tsx index b3810f16..f79c018a 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; 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 { isAllView, isModView, 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'; @@ -30,6 +30,7 @@ const BoardLayout = () => { const isMobile = useIsMobile(); const isInAllView = isAllView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); + const isInModView = isModView(location.pathname); const pendingPost = useAccountComment({ commentIndex: accountCommentIndex ? parseInt(accountCommentIndex) : undefined }); // Christmas theme @@ -53,17 +54,17 @@ const BoardLayout = () => { {isMobile - ? (subplebbitAddress || isInAllView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && ( + ? (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && ( <>
) - : (subplebbitAddress || isInAllView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && ( + : (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && ( <> - {!(isInAllView || isInSubscriptionsView) && } + {!(isInAllView || isInSubscriptionsView || isInModView) && } )} diff --git a/src/components/board-header/board-header.tsx b/src/components/board-header/board-header.tsx index c9372328..d2abb9e6 100644 --- a/src/components/board-header/board-header.tsx +++ b/src/components/board-header/board-header.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useLocation, useParams } from 'react-router-dom'; import { useAccountComment } from '@plebbit/plebbit-react-hooks'; import Plebbit from '@plebbit/plebbit-js'; @@ -10,6 +11,7 @@ import useIsMobile from '../../hooks/use-is-mobile'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; import { shouldShowSnow } from '../../lib/snow'; import Tooltip from '../tooltip'; +import _ from 'lodash'; const totalBanners = 63; @@ -23,6 +25,7 @@ const ImageBanner = () => { }; const BoardHeader = () => { + const { t } = useTranslation(); const location = useLocation(); const params = useParams(); const isInAllView = isAllView(location.pathname); @@ -36,7 +39,13 @@ const BoardHeader = () => { const { address, shortAddress } = subplebbit || {}; const multisubMetadata = useMultisubMetadata(); - const title = isInAllView ? multisubMetadata?.title || 'all' : isInSubscriptionsView ? 'Subscriptions' : isInModView ? 'Mod' : subplebbit?.title; + const title = isInAllView + ? multisubMetadata?.title || 'all' + : isInSubscriptionsView + ? 'Subscriptions' + : isInModView + ? _.startCase(t('boards_you_moderate')) + : subplebbit?.title; const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : isInModView ? 'p/mod' : `p/${address}`; const { isOffline, isOnlineStatusLoading, offlineIconClass, offlineTitle } = useIsSubplebbitOffline(subplebbit); diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index 1d107805..f66352a8 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -1,13 +1,14 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; -import { Comment, setAccount, useAccount, useAccountComment, useEditedComment } from '@plebbit/plebbit-react-hooks'; +import { Comment, setAccount, useAccount, useAccountComment, useAccountSubplebbits, useEditedComment } from '@plebbit/plebbit-react-hooks'; +import Plebbit from '@plebbit/plebbit-js'; import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits'; import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages'; import { getHasThumbnail, getLinkMediaInfo } from '../../lib/utils/media-utils'; import { formatMarkdown } from '../../lib/utils/post-utils'; import { isValidURL } from '../../lib/utils/url-utils'; -import { isAllView, isDescriptionView, isPostPageView, isRulesView, isSubscriptionsView } from '../../lib/utils/view-utils'; +import { isAllView, isDescriptionView, isModView, isPostPageView, isRulesView, isSubscriptionsView } from '../../lib/utils/view-utils'; import useAnonMode from '../../hooks/use-anon-mode'; import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame'; @@ -53,10 +54,14 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: const location = useLocation(); const isInAllView = isAllView(location.pathname); + const isInModView = isModView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const subscriptions = account?.subscriptions || []; const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); + const { accountSubplebbits } = useAccountSubplebbits(); + const accountSubplebbitAddresses = Object.keys(accountSubplebbits); + const { anonMode, getNewSigner, getExistingSigner } = useAnonMode(postCid); const comment = useSubplebbitsPagesStore((state) => state.comments[postCid]); const address = comment?.author?.address; @@ -129,7 +134,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: return; } - if ((isInAllView || isInSubscriptionsView) && !publishPostOptions.subplebbitAddress) { + if ((isInAllView || isInSubscriptionsView || isInModView) && !publishPostOptions.subplebbitAddress) { alert(t('no_board_selected_warning')); return; } @@ -394,7 +399,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: ] - {(isInAllView || isInSubscriptionsView) && ( + {(isInAllView || isInSubscriptionsView || isInModView) && ( {t('board')} @@ -403,7 +408,13 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: {isInAllView && defaultSubplebbitAddresses.map((address: string) => ( + ))} + {isInModView && + accountSubplebbitAddresses.map((address: string) => ( + ))} {isInSubscriptionsView && @@ -429,6 +440,7 @@ const PostForm = () => { const isInPostView = isPostPageView(location.pathname, params); const isInRulesView = isRulesView(location.pathname, params); const isInAllView = isAllView(location.pathname); + const isInModView = isModView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); const commentCid = params?.commentCid; @@ -453,7 +465,9 @@ const PostForm = () => { return ( <>
- {!(isInAllView || isInSubscriptionsView) && showForm && (isOffline || isOnlineStatusLoading) &&
{offlineTitle}
} + {!(isInAllView || isInSubscriptionsView || isInModView) && showForm && (isOffline || isOnlineStatusLoading) && ( +
{offlineTitle}
+ )} {isThreadClosed ? (
{t('thread_closed')} @@ -473,7 +487,9 @@ const PostForm = () => { )}
- {!(isInAllView || isInSubscriptionsView) && showForm && (isOffline || isOnlineStatusLoading) &&
{offlineTitle}
} + {!(isInAllView || isInSubscriptionsView || isInModView) && showForm && (isOffline || isOnlineStatusLoading) && ( +
{offlineTitle}
+ )} {isThreadClosed ? (
{t('thread_closed')} diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index b86487d8..d3b710a3 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -148,15 +148,17 @@ const Catalog = () => { } return true; }) - .map((subplebbit) => subplebbit.address); + .map((subplebbit) => subplebbit.address) + .filter(Boolean); // Filter out any undefined/null values if (isInAllView) { return filteredDefaultSubplebbits; } if (isInSubscriptionsView) { - return subscriptions || []; + return (subscriptions || []).filter(Boolean); // Filter out any undefined/null values } - return [subplebbitAddress]; + // Only include subplebbitAddress if it's defined + return subplebbitAddress ? [subplebbitAddress] : []; }, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, hideAdultBoards, hideGoreBoards]); const { imageSize } = useCatalogStyleStore();