import { useEffect, useRef, useState } from 'react'; import styles from './home.module.css'; import { Link, useNavigate } from 'react-router-dom'; import { Trans, useTranslation } from 'react-i18next'; import { Comment, Subplebbit, useAccount, useAccountSubplebbits, useSubplebbits } from '@plebbit/plebbit-react-hooks'; import packageJson from '../../../package.json'; import useDefaultSubplebbits, { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; import usePopularPosts from '../../hooks/use-popular-posts'; import { getCommentMediaInfo } from '../../lib/utils/media-utils'; import { CatalogPostMedia } from '../../components/catalog-row'; import LoadingEllipsis from '../../components/loading-ellipsis'; import { shuffle } from 'lodash'; // for temporary hook to fetch subplebbit stats import { useMemo } from 'react'; import { create } from 'zustand'; const isValidAddress = (address: string): boolean => { if (address.includes('/') || address.includes('\\') || address.includes(' ')) { return false; } return true; }; const SearchBar = () => { const searchInputRef = useRef(null); const { t } = useTranslation(); const navigate = useNavigate(); const handleSearchSubmit = () => { const inputValue = searchInputRef.current?.value; if (inputValue && isValidAddress(inputValue)) { navigate(`/p/${inputValue}`); } else { alert('invalid address'); } }; return (
{ if (e.key === 'Enter') { handleSearchSubmit(); } }} />
); }; const InfoBox = () => { const { t } = useTranslation(); return (

{t('what_is_plebchan')}

}} />

{t('no_global_rules_info')}
); }; const Board = ({ isOffline, subplebbit }: { isOffline: boolean; subplebbit: Subplebbit }) => { const { t } = useTranslation(); const { address, title, tags } = subplebbit; const nsfwTags = ['adult', 'gore']; const nsfwTag = tags.find((tag: string) => nsfwTags.includes(tag)); return (
{title || address} {nsfwTag && ({t(nsfwTag)})} {isOffline && }
); }; const Boards = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebbits: any }) => { const { t } = useTranslation(); const account = useAccount(); const subscriptions = account?.subscriptions || []; const { accountSubplebbits } = useAccountSubplebbits(); const accountSubplebbitAddresses = Object.keys(accountSubplebbits); const plebbitSubs = multisub.filter((sub) => sub.tags.includes('plebbit')); const interestsSubs = multisub.filter( (sub) => sub.tags.includes('topic') && !sub.tags.includes('plebbit') && !sub.tags.includes('country') && !sub.tags.includes('international'), ); const randomSubs = multisub.filter((sub) => sub.tags.includes('random') && !sub.tags.includes('plebbit')); const internationalSubs = multisub.filter((sub) => sub.tags.includes('international') || sub.tags.includes('country')); const projectsSubs = multisub.filter((sub) => sub.tags.includes('project') && !sub.tags.includes('plebbit') && !sub.tags.includes('topic')); const isSubOffline = (address: string) => { const subplebbit = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address); const isOffline = subplebbit?.updatedAt && subplebbit.updatedAt < Date.now() / 1000 - 60 * 60; return isOffline; }; return (

{t('boards')}

{t('options')} ▼

Plebbit

{plebbitSubs.map((sub) => ( ))}

{t('projects')}

{projectsSubs.map((sub) => ( ))}

{t('interests')}

{interestsSubs.map((sub) => ( ))}

{t('random')}

{randomSubs.map((sub) => ( ))}

{t('international')}

{internationalSubs.map((sub) => ( ))}

{t('subscriptions')}

{subscriptions.length > 0 ? subscriptions.map((address: string, index: number) => (
{address}
)) : t('not_subscribed')}

{t('moderating')}

{accountSubplebbitAddresses.length > 0 ? accountSubplebbitAddresses.map((address: string, index: number) => (
p/{address}
)) : t('not_moderating')}
); }; interface PopularThreadProps { post: Comment; boardTitle: string | undefined; boardShortAddress: string; } const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThreadProps) => { const { cid, content, subplebbitAddress, title } = post || {}; const commentMediaInfo = getCommentMediaInfo(post); return (
{boardTitle || boardShortAddress}
{title && {title}} {content && (content.length > 99 ? `: ${content.substring(0, 99)}...` : `: ${content}`)}
); }; const PopularThreads = ({ subplebbits }: { subplebbits: any }) => { const { t } = useTranslation(); const popularPosts = usePopularPosts(subplebbits); return (

{t('popular_threads')}

{t('options')} ▼
{popularPosts.length < 8 ? ( ) : ( popularPosts.map((post: any) => ( )) )}
); }; // temporary hook to fetch subplebbit stats function useSubplebbitsStats(options: any) { const { subplebbitAddresses, accountName } = options || {}; const account = useAccount({ accountName }); const { subplebbits } = useSubplebbits({ subplebbitAddresses }); const { setSubplebbitStats, subplebbitsStats } = useSubplebbitsStatsStore(); useEffect(() => { if (!subplebbitAddresses || subplebbitAddresses.length === 0 || !account) { return; } subplebbits.forEach((subplebbit) => { if (subplebbit && subplebbit.statsCid && !subplebbitsStats[subplebbit.address]) { account.plebbit .fetchCid(subplebbit.statsCid) .then((fetchedStats: any) => { setSubplebbitStats(subplebbit.address, JSON.parse(fetchedStats)); }) .catch((error: any) => { console.error('Fetching subplebbit stats failed', { subplebbitAddress: subplebbit.address, error }); }); } }); }, [account, subplebbits, setSubplebbitStats, subplebbitsStats, subplebbitAddresses]); return useMemo(() => { return subplebbitAddresses.reduce((acc: any, address: any) => { acc[address] = subplebbitsStats[address] || { loading: true }; return acc; }, {}); }, [subplebbitsStats, subplebbitAddresses]); } export type SubplebbitsStatsState = { subplebbitsStats: { [subplebbitAddress: string]: any }; setSubplebbitStats: Function; }; const useSubplebbitsStatsStore = create((set) => ({ subplebbitsStats: {}, setSubplebbitStats: (subplebbitAddress: string, subplebbitStats: any) => set((state) => ({ subplebbitsStats: { ...state.subplebbitsStats, [subplebbitAddress]: subplebbitStats }, })), })); const Stats = ({ multisub, subplebbitAddresses }: { multisub: any; subplebbitAddresses: string[] }) => { const { t } = useTranslation(); const stats = useSubplebbitsStats({ subplebbitAddresses }); const allStatsLoaded = useMemo(() => { return subplebbitAddresses.every((address) => stats[address]); }, [stats, subplebbitAddresses]); const { totalPosts, currentUsers } = useMemo(() => { let totalPosts = 0; let currentUsers = 0; if (allStatsLoaded) { Object.values(stats).forEach((stat: any) => { totalPosts += stat.allPostCount || 0; currentUsers += stat.weekActiveUserCount || 0; }); } return { totalPosts, currentUsers }; }, [stats, allStatsLoaded]); const boardsTracked = Object.values(stats).filter((stat: any) => stat && !stat.loading).length; const enoughStats = boardsTracked >= multisub.length / 2; return (

{t('stats')}

{/*

{t('stats_info')}

*/} {enoughStats ? ( <>
{t('total_posts')} {totalPosts}
{t('current_users')} {currentUsers}
{t('boards_tracked')} {boardsTracked}
) : ( )}
); }; const { version } = packageJson; const downloadAppLink = (() => { const platform = navigator.platform; if (platform === 'Linux' || platform === 'Linux x86_64' || platform === 'Linux i686' || platform === 'Linux aarch64') { return `https://github.com/plebbit/plebchan/releases/download/v${version}/plebchan-${version}.AppImage`; } else if (platform === 'Win32' || platform === 'Win64' || platform === 'Windows') { return `https://github.com/plebbit/plebchan/releases/download/v${version}/plebchan.Portable.${version}.exe`; } else if (platform === 'MacIntel' || platform === 'Macintosh') { return `https://github.com/plebbit/plebchan/releases/download/v${version}/plebchan-${version}.dmg`; } else if (platform === 'Android') { return undefined; } else if (platform === 'iPhone' || platform === 'iPad') { return undefined; } else { return undefined; } })(); const isElectron = window.isElectron === true; const commitRef = process.env.REACT_APP_COMMIT_REF; const Footer = () => { const { t } = useTranslation(); return ( <>
v{packageJson.version} {isElectron && ( node stats )} {commitRef && ( #{commitRef.slice(0, 7)} )}
); }; export const HomeLogo = () => { return (
); }; const Home = () => { const defaultSubplebbits = useDefaultSubplebbits(); const subplebbitAddresses = useDefaultSubplebbitAddresses(); const { subplebbits } = useSubplebbits({ subplebbitAddresses }); return (
); }; export default Home;