import { useTranslation } from 'react-i18next'; import { useLocation, useParams } from 'react-router-dom'; import { useComment } from '@bitsocial/bitsocial-react-hooks'; import BoardsBar from '../boards-bar'; import SiteLegalMeta from '../site-legal-meta'; import StyleSelector from '../style-selector/style-selector'; import { CatalogSearchResultsLabel, 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 useThreadLiveUpdatesStore from '../../stores/use-thread-live-updates-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 { useCommunityIdentifier } from '../../hooks/use-community-identifiers'; import capitalize from 'lodash/capitalize'; import styles from './footer.module.css'; /* ----------------------------------------------------------------------------- * PageFooterDesktop * Main page footer wrapper: hr, first row slot, BoardsBar, SiteLegalMeta. * -------------------------------------------------------------------------- */ interface PageFooterDesktopProps { /** Mode-specific first row content (e.g. board pagination or thread controls) */ firstRow?: React.ReactNode; /** Optional row between first row and BoardsBar (e.g. style selector on thread page) */ styleRow?: React.ReactNode; /** Catalog pages omit the default footer top margin */ variant?: 'catalog'; } export const PageFooterDesktop = ({ firstRow, styleRow, variant }: PageFooterDesktopProps) => ( ); /* ----------------------------------------------------------------------------- * StyleOnlyFooterFirstRow * Footer first row with only the style selector (right-aligned). Used by mod queue. * -------------------------------------------------------------------------- */ export const StyleOnlyFooterFirstRow = () => { const { t } = useTranslation(); return (
{t('style')}:
); }; /* ----------------------------------------------------------------------------- * CatalogFooterFirstRow * Catalog footer nav row: Return, Catalog, Top, Refresh, plus search/filter label. * -------------------------------------------------------------------------- */ interface CatalogFooterFirstRowProps { communityAddress?: string; isInAllView?: boolean; isInSubscriptionsView?: boolean; isInModView?: boolean; } export const CatalogFooterFirstRow = ({ communityAddress, isInAllView = false, isInSubscriptionsView = false, isInModView = false }: CatalogFooterFirstRowProps) => { return (
[] [] [] []
); }; /* ----------------------------------------------------------------------------- * CatalogFooterStyleRow * Catalog style selector on its own row (below nav, above BoardsBar). * -------------------------------------------------------------------------- */ export const CatalogFooterStyleRow = () => { const { t } = useTranslation(); return ( {t('style')}: ); }; /* ----------------------------------------------------------------------------- * ThreadFooterStyleRow * Style selector on its own row (thread page only, below first row, above BoardsBar). * -------------------------------------------------------------------------- */ export const ThreadFooterStyleRow = () => { const { t } = useTranslation(); return ( {t('style')}: ); }; /* ----------------------------------------------------------------------------- * ThreadFooterFirstRow * Thread page footer first row: Return, Catalog, Top, Update, Auto, Post a Reply, stats. * -------------------------------------------------------------------------- */ interface ThreadFooterFirstRowProps { postCid: string; threadNumber: number | undefined; communityAddress: string; /** Thread closed - disable Post a Reply */ isThreadClosed?: boolean; } export const ThreadFooterFirstRow = ({ postCid, threadNumber, communityAddress, isThreadClosed = false }: ThreadFooterFirstRowProps) => { 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 handlePostReplyClick = () => { if (isThreadClosed) return; openReplyModalEmpty(postCid, threadNumber, communityAddress); }; return (
[] [] [] [] []
[ ]
); }; /* ----------------------------------------------------------------------------- * PageFooterMobile * Mobile-only page footer with content slot and footer links. * -------------------------------------------------------------------------- */ export const PageFooterMobile = ({ children }: { children: React.ReactNode }) => ( ); /* ----------------------------------------------------------------------------- * ThreadFooterMobile * Mobile thread page footer: Post a Reply, Return/Catalog/Top, Update/Auto, stats. * -------------------------------------------------------------------------- */ interface ThreadFooterMobileProps { postCid: string; threadNumber: number | undefined; communityAddress: string; isThreadClosed?: boolean; } export const ThreadFooterMobile = ({ postCid, threadNumber, communityAddress, isThreadClosed = false }: ThreadFooterMobileProps) => { const { t } = useTranslation(); const location = useLocation(); const params = useParams(); const { openReplyModalEmpty } = useReplyModalStore(); const autoUpdateEnabled = useThreadLiveUpdatesStore((state) => state.enabled); const isInAllView = isAllView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); const isInModView = isModView(location.pathname); const communityIdentifier = useCommunityIdentifier(communityAddress); const post = useComment({ commentCid: postCid, autoUpdate: autoUpdateEnabled, community: communityIdentifier }); const { replyCount } = post || {}; const linkCount = useCountLinksInReplies(post); const directoryEntry = useDirectoryByAddress(communityAddress); const requirePostLinkIsMedia = directoryEntry?.features?.requirePostLinkIsMedia === true; const pageNumber = usePostPageNumber({ communityAddress: communityAddress, postCid, enabled: true }); const handlePostReplyClick = () => { if (isThreadClosed) return; openReplyModalEmpty(postCid, threadNumber, communityAddress); }; return (
{capitalize(t('replies'))}: {replyCount ?? '?'} / {capitalize(requirePostLinkIsMedia ? t('images') : t('links'))}: {linkCount ?? '?'} /{' '} {t('pagination.pageLabel')}: {pageNumber ?? '?'}
); };