From 84e6057bb84422e41d89dc9b822d8621140c7fe8 Mon Sep 17 00:00:00 2001 From: plebeius Date: Tue, 3 Mar 2026 15:07:14 +0800 Subject: [PATCH] feat(post): add mobile footer to thread view Post a Reply, Return/Catalog/Top, Update/Auto buttons, stats row (Replies/Images/Page), and license footer links. Matches mobile board view styling. --- src/components/footer/footer.module.css | 33 +++++++++++++ src/components/footer/footer.tsx | 65 +++++++++++++++++++++++++ src/components/footer/index.ts | 4 +- src/views/post/post.tsx | 13 +++-- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/src/components/footer/footer.module.css b/src/components/footer/footer.module.css index 9c73fe95..9fca4271 100644 --- a/src/components/footer/footer.module.css +++ b/src/components/footer/footer.module.css @@ -175,6 +175,19 @@ padding: 8px 8px; } +.mobileFooterButtons a { + all: unset; +} + +.mobileFooterButtons input[type='checkbox'] { + margin: 0 3px 0 0; + position: relative; + top: -2px; + border-radius: 0; + height: 10px; + width: 10px; +} + .mobileFooterPagination { display: flex; flex-wrap: wrap; @@ -209,3 +222,23 @@ .mobileFooterLinks a:hover { color: var(--boardsbar-desktop-link-text-color-hover); } + +.mobileFooterStats { + padding: 4px 8px; + font-size: 14px; + text-align: center; +} + +.threadMobileFooterContent .mobileFooterButtons { + padding-top: 0; + padding-bottom: 6px; +} + +.threadMobileFooterContent .mobileFooterButtons:first-child { + padding-top: 25px; + padding-bottom: 10px; +} + +.threadMobileFooterContent .mobileFooterStats { + padding-top: 15px; +} diff --git a/src/components/footer/footer.tsx b/src/components/footer/footer.tsx index 80d92c80..7d66c17f 100644 --- a/src/components/footer/footer.tsx +++ b/src/components/footer/footer.tsx @@ -1,11 +1,16 @@ import { useTranslation } from 'react-i18next'; import { useLocation, useParams } from 'react-router-dom'; +import { useComment } from '@bitsocialhq/pkc-react-hooks'; import BoardsBar from '../boards-bar'; import SiteLegalMeta from '../site-legal-meta'; import StyleSelector from '../style-selector/style-selector'; import { ReturnButton, CatalogButton, TopButton, UpdateButton, AutoButton, PostPageStats, RefreshButton } from '../board-buttons/board-buttons'; import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils'; import useReplyModalStore from '../../stores/use-reply-modal-store'; +import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; +import { usePostPageNumber } from '../../hooks/use-post-page-number'; +import { useDirectoryByAddress } from '../../hooks/use-directories'; +import capitalize from 'lodash/capitalize'; import styles from './footer.module.css'; /* ----------------------------------------------------------------------------- @@ -165,3 +170,63 @@ export const PageFooterMobile = ({ children }: { children: React.ReactNode }) => ); + +/* ----------------------------------------------------------------------------- + * ThreadFooterMobile + * Mobile thread page footer: Post a Reply, Return/Catalog/Top, Update/Auto, stats. + * -------------------------------------------------------------------------- */ + +export interface ThreadFooterMobileProps { + postCid: string; + threadNumber: number | undefined; + subplebbitAddress: string; + isThreadClosed?: boolean; +} + +export const ThreadFooterMobile = ({ postCid, threadNumber, subplebbitAddress, isThreadClosed = false }: ThreadFooterMobileProps) => { + const { t } = useTranslation(); + const location = useLocation(); + const params = useParams(); + const { openReplyModalEmpty } = useReplyModalStore(); + + const isInAllView = isAllView(location.pathname); + const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); + const isInModView = isModView(location.pathname); + + const post = useComment({ commentCid: postCid }); + const { replyCount } = post || {}; + const linkCount = useCountLinksInReplies(post); + const directoryEntry = useDirectoryByAddress(subplebbitAddress); + const requirePostLinkIsMedia = directoryEntry?.features?.requirePostLinkIsMedia === true; + const pageNumber = usePostPageNumber({ subplebbitAddress, postCid, enabled: true }); + + const handlePostReplyClick = () => { + if (isThreadClosed) return; + openReplyModalEmpty(postCid, threadNumber, subplebbitAddress); + }; + + return ( + +
+
+ +
+
+ + + +
+
+ + +
+
+ {capitalize(t('replies'))}: {replyCount ?? '?'} / {capitalize(requirePostLinkIsMedia ? t('images') : t('links'))}: {linkCount ?? '?'} /{' '} + {t('pagination.pageLabel')}: {pageNumber ?? '?'} +
+
+
+ ); +}; diff --git a/src/components/footer/index.ts b/src/components/footer/index.ts index d39758eb..a0bfe669 100644 --- a/src/components/footer/index.ts +++ b/src/components/footer/index.ts @@ -1,2 +1,2 @@ -export { PageFooterDesktop, PageFooterMobile, CatalogFooterFirstRow, ThreadFooterFirstRow, ThreadFooterStyleRow } from './footer'; -export type { PageFooterDesktopProps, CatalogFooterFirstRowProps, ThreadFooterFirstRowProps } from './footer'; +export { PageFooterDesktop, PageFooterMobile, CatalogFooterFirstRow, ThreadFooterFirstRow, ThreadFooterStyleRow, ThreadFooterMobile } from './footer'; +export type { PageFooterDesktopProps, CatalogFooterFirstRowProps, ThreadFooterFirstRowProps, ThreadFooterMobileProps } from './footer'; diff --git a/src/views/post/post.tsx b/src/views/post/post.tsx index eb43cc88..4a7d8db1 100644 --- a/src/views/post/post.tsx +++ b/src/views/post/post.tsx @@ -10,7 +10,7 @@ import { useDirectories } from '../../hooks/use-directories'; import { isDirectoryBoard } from '../../lib/utils/route-utils'; import useIsMobile from '../../hooks/use-is-mobile'; import ErrorDisplay from '../../components/error-display/error-display'; -import { PageFooterDesktop, ThreadFooterFirstRow, ThreadFooterStyleRow } from '../../components/footer'; +import { PageFooterDesktop, ThreadFooterFirstRow, ThreadFooterStyleRow, ThreadFooterMobile } from '../../components/footer'; import PostDesktop from '../../components/post-desktop'; import PostMobile from '../../components/post-mobile'; import styles from './post.module.css'; @@ -203,10 +203,13 @@ const PostPage = () => { )} {post?.cid && subplebbitAddress ? ( - } - styleRow={} - /> + <> + } + styleRow={} + /> + + ) : null} );