diff --git a/AGENTS.md b/AGENTS.md index 64fe4541f..e8f6fefb8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,7 @@ seedit is a serverless, adminless, decentralized Reddit-style client built on th - Zustand for shared state - React Router v6 - Vite -- `@bitsocialnet/bitsocial-react-hooks` +- `@bitsocial/bitsocial-react-hooks` - i18next - Corepack-managed Yarn 4 - oxlint @@ -77,7 +77,7 @@ src/ ### React Architecture Rules - Do not use `useState` for shared/global state. Use Zustand stores in `src/stores/`. -- Do not use `useEffect` for data fetching. Use `@bitsocialnet/bitsocial-react-hooks`. +- Do not use `useEffect` for data fetching. Use `@bitsocial/bitsocial-react-hooks`. - Do not sync derived state with effects. Compute during render. - Avoid copy-paste logic across components. Extract custom hooks in `src/hooks/`. - Avoid boolean flag soup for complex flows; model state clearly in Zustand. diff --git a/package.json b/package.json index 30d9f6fbb..e618e5192 100755 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "private": true, "packageManager": "yarn@4.13.0", "dependencies": { - "@bitsocialnet/bitsocial-react-hooks": "https://codeload.github.com/bitsocialnet/bitsocial-react-hooks/tar.gz/dcfaeaccefc0b9de90ef37c44885ac005eef429a", + "@bitsocial/bitsocial-react-hooks": "0.1.2", "@capacitor/app": "7.0.1", "@capacitor/filesystem": "7.1.4", "@capacitor/local-notifications": "7.0.1", @@ -30,11 +30,10 @@ "fs-extra": "11.2.0", "gifuct-js": "2.1.2", "http-proxy": "1.18.1", - "i18next": "23.5.1", + "i18next": "25.10.9", "i18next-browser-languagedetector": "7.1.0", - "i18next-http-backend": "2.2.2", + "i18next-http-backend": "3.0.5", "json-stringify-pretty-compact": "4.0.0", - "kubo": "0.39.0", "lodash": "4.18.0", "memoizee": "0.4.15", "node-fetch": "2", @@ -42,7 +41,7 @@ "react-ace": "14.0.1", "react-dom": "19.1.2", "react-dropzone": "14.3.8", - "react-i18next": "13.2.2", + "react-i18next": "16.6.6", "react-markdown": "10.1.0", "react-router-dom": "6.30.2", "react-router-hash-link": "2.4.3", @@ -56,7 +55,6 @@ "zustand": "4.4.3" }, "scripts": { - "postinstall": "node scripts/patch-bitsocial-react-hooks-esm.cjs", "start": "node scripts/start-dev.js", "build": "cross-env NODE_ENV=production PUBLIC_URL=./ GENERATE_SOURCEMAP=false vite build", "build:preload": "cross-env NODE_ENV=production vite build --config electron/vite.preload.config.js", @@ -114,7 +112,7 @@ "@types/memoizee": "0.4.9", "@types/node-fetch": "2", "@typescript/native-preview": "7.0.0-dev.20260115.1", - "@vitejs/plugin-react": "4.3.4", + "@vitejs/plugin-react": "6.0.0", "assert": "2.1.0", "babel-plugin-react-compiler": "1.0.0", "buffer": "6.0.3", diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 45894edbd..10fd0dabc 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -293,8 +293,8 @@ "more_posts_last_month": "{{count}} posts last {{currentTimeFilterName}}: <1>show more posts from last month", "profile_info": "Your account u/{{shortAddress}} was created. <1>Set display name, <2>export backup, <3>learn more.", "show_all_instead": "Showing posts since {{timeFilterName}}, <1>show all instead", - "subplebbit_offline_info": "The subplebbit might be offline and publishing might fail.", - "posts_last_synced_info": "Posts last synced {{time}}, the subplebbit might be offline and publishing might fail.", + "subplebbit_offline_info": "The community might be offline and publishing might fail.", + "posts_last_synced_info": "Posts last synced {{time}}, the community might be offline and publishing might fail.", "import_account_backup": "<1>import account backup", "export_account_backup": "<1>export account backup", "save_reset_changes": "<1>save or <2>reset changes", diff --git a/scripts/patch-bitsocial-react-hooks-esm.cjs b/scripts/patch-bitsocial-react-hooks-esm.cjs deleted file mode 100644 index 12cad2161..000000000 --- a/scripts/patch-bitsocial-react-hooks-esm.cjs +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -const packageDistPath = path.join(__dirname, '..', 'node_modules', '@bitsocialnet', 'bitsocial-react-hooks', 'dist'); -const logPrefix = '[patch-bitsocial-react-hooks-esm]'; - -if (!fs.existsSync(packageDistPath)) { - console.log(`${logPrefix} Skip: @bitsocialnet/bitsocial-react-hooks dist not found.`); - process.exit(0); -} - -const relativeImportPattern = /(from\s+|import\s+)(['"])(\.\.?\/[^'"]+)\2/g; -let touchedFiles = 0; -let rewrittenImports = 0; -let appliedHardeningPatches = 0; - -const splitSpecifier = (specifier) => { - const suffixStart = specifier.search(/[?#]/); - - if (suffixStart === -1) { - return { bareSpecifier: specifier, suffix: '' }; - } - - return { - bareSpecifier: specifier.slice(0, suffixStart), - suffix: specifier.slice(suffixStart), - }; -}; - -const resolveSpecifier = (filePath, specifier) => { - const { bareSpecifier, suffix } = splitSpecifier(specifier); - - if (path.extname(bareSpecifier)) { - return null; - } - - const absoluteSpecifierPath = path.resolve(path.dirname(filePath), bareSpecifier); - - if (fs.existsSync(`${absoluteSpecifierPath}.js`)) { - return `${bareSpecifier}.js${suffix}`; - } - - if (fs.existsSync(path.join(absoluteSpecifierPath, 'index.js'))) { - return `${bareSpecifier}/index.js${suffix}`; - } - - return null; -}; - -const patchFile = (filePath) => { - const source = fs.readFileSync(filePath, 'utf8'); - let fileImportCount = 0; - - const updated = source.replace(relativeImportPattern, (match, prefix, quote, specifier) => { - const resolvedSpecifier = resolveSpecifier(filePath, specifier); - - if (!resolvedSpecifier || resolvedSpecifier === specifier) { - return match; - } - - fileImportCount += 1; - return `${prefix}${quote}${resolvedSpecifier}${quote}`; - }); - - if (!fileImportCount) { - return; - } - - fs.writeFileSync(filePath, updated, 'utf8'); - touchedFiles += 1; - rewrittenImports += fileImportCount; -}; - -const patchLegacyAccountMigration = () => { - const accountsDatabasePath = path.join(packageDistPath, 'stores', 'accounts', 'accounts-database.js'); - - if (!fs.existsSync(accountsDatabasePath)) { - return; - } - - const source = fs.readFileSync(accountsDatabasePath, 'utf8'); - - if (source.includes('account.communities = legacyCommunities;')) { - return; - } - - const migrationNeedle = ` }\n account.version = accountVersion;\n return account;\n});`; - const migrationPatch = ` }\n if (!Array.isArray(account.subscriptions)) {\n account.subscriptions = [];\n }\n if (!account.blockedAddresses || typeof account.blockedAddresses !== "object") {\n account.blockedAddresses = {};\n }\n if (!account.blockedCids || typeof account.blockedCids !== "object") {\n account.blockedCids = {};\n }\n if (!account.communities || typeof account.communities !== "object") {\n const legacyCommunities = account.subplebbits && typeof account.subplebbits === "object" ? account.subplebbits : {};\n account.communities = legacyCommunities;\n }\n account.version = accountVersion;\n return account;\n});`; - - if (!source.includes(migrationNeedle)) { - console.warn(`${logPrefix} Skip: could not find legacy account migration patch location.`); - return; - } - - fs.writeFileSync(accountsDatabasePath, source.replace(migrationNeedle, migrationPatch), 'utf8'); - appliedHardeningPatches += 1; -}; - -const patchAccountsCommunitiesHardening = () => { - const accountsActionsInternalPath = path.join(packageDistPath, 'stores', 'accounts', 'accounts-actions-internal.js'); - const accountsUtilsPath = path.join(packageDistPath, 'stores', 'accounts', 'utils.js'); - - if (fs.existsSync(accountsActionsInternalPath)) { - const source = fs.readFileSync(accountsActionsInternalPath, 'utf8'); - - if (!source.includes('const accountCommunities = account.communities && typeof account.communities === "object" ? account.communities : {};')) { - const needle = ` const role = getRole(community, account.author.address);\n if (!role) {\n if (account.communities[community.address]) {\n toRemove.push(accountId);\n }\n }\n else {\n const currentRole = (_a = account.communities[community.address]) === null || _a === void 0 ? void 0 : _a.role;`; - const replacement = ` const role = getRole(community, account.author.address);\n const accountCommunities = account.communities && typeof account.communities === "object" ? account.communities : {};\n if (!role) {\n if (accountCommunities[community.address]) {\n toRemove.push(accountId);\n }\n }\n else {\n const currentRole = (_a = accountCommunities[community.address]) === null || _a === void 0 ? void 0 : _a.role;`; - - if (source.includes(needle)) { - fs.writeFileSync(accountsActionsInternalPath, source.replace(needle, replacement), 'utf8'); - appliedHardeningPatches += 1; - } else { - console.warn(`${logPrefix} Skip: could not find accounts-actions-internal hardening patch location.`); - } - } - } - - if (fs.existsSync(accountsUtilsPath)) { - const source = fs.readFileSync(accountsUtilsPath, 'utf8'); - - if (!source.includes('const storedAccountCommunities = account.communities && typeof account.communities === "object"')) { - const needle = ` const roles = getAuthorAddressRolesFromCommunities(account.author.address, communities);\n const accountCommunities = Object.assign({}, account.communities);`; - const replacement = ` const roles = getAuthorAddressRolesFromCommunities(account.author.address, communities);\n const storedAccountCommunities = account.communities && typeof account.communities === "object"\n ? account.communities\n : account.subplebbits && typeof account.subplebbits === "object"\n ? account.subplebbits\n : {};\n const accountCommunities = Object.assign({}, storedAccountCommunities);`; - - if (source.includes(needle)) { - fs.writeFileSync(accountsUtilsPath, source.replace(needle, replacement), 'utf8'); - appliedHardeningPatches += 1; - } else { - console.warn(`${logPrefix} Skip: could not find accounts-utils hardening patch location.`); - } - } - } -}; - -const walk = (currentPath) => { - for (const entry of fs.readdirSync(currentPath, { withFileTypes: true })) { - const entryPath = path.join(currentPath, entry.name); - - if (entry.isDirectory()) { - walk(entryPath); - continue; - } - - if (entry.isFile() && entry.name.endsWith('.js')) { - patchFile(entryPath); - } - } -}; - -walk(packageDistPath); -patchLegacyAccountMigration(); -patchAccountsCommunitiesHardening(); - -if (!touchedFiles && !appliedHardeningPatches) { - console.log(`${logPrefix} No relative ESM imports or account migration patches were needed.`); - process.exit(0); -} - -console.log( - `${logPrefix} Patched ${rewrittenImports} imports across ${touchedFiles} files and applied ${appliedHardeningPatches} account migration hardening patch(es).`, -); diff --git a/src/AGENTS.md b/src/AGENTS.md index 424815329..5bcf16176 100644 --- a/src/AGENTS.md +++ b/src/AGENTS.md @@ -4,6 +4,6 @@ These rules apply to `src/**`. Follow the repo-root `AGENTS.md` first, then use - Keep route composition in `src/views/`, reusable UI in `src/components/`, shared logic in `src/hooks/`, and shared app state in `src/stores/`. - Before adding new state, decide whether it belongs in render, a reusable hook, or a Zustand store. Do not duplicate the same state logic across views. -- Use `@bitsocialnet/bitsocial-react-hooks` for data access. Do not add data-fetching `useEffect` calls or effects that only synchronize derived state. +- Use `@bitsocial/bitsocial-react-hooks` for data access. Do not add data-fetching `useEffect` calls or effects that only synchronize derived state. - When changing React UI logic, run `yarn build`, `yarn lint`, and `yarn type-check`. When changing layout or interaction, verify desktop and mobile behavior with `playwright-cli`. - Prefer extending nearby tests under `src/**/__tests__/` when touching already-covered behavior. diff --git a/src/app.tsx b/src/app.tsx index 2ed126a1c..1879e819b 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -15,11 +15,11 @@ import PostPage from './views/post-page'; import Profile from './views/profile'; import Settings from './views/settings'; import AccountDataEditor from './views/settings/account-data-editor'; -import SubplebbitDataEditor from './views/subplebbit-settings/subplebbit-data-editor'; +import CommunityDataEditor from './views/community-settings/community-data-editor'; import SubmitPage from './views/submit-page'; -import Subplebbit from './views/subplebbit'; -import SubplebbitSettings from './views/subplebbit-settings'; -import Subplebbits from './views/subplebbits'; +import CommunityView from './views/community'; +import CommunitySettings from './views/community-settings'; +import Communities from './views/communities'; import AccountBar from './components/account-bar/'; import ChallengeModal from './components/challenge-modal'; import Header from './components/header'; @@ -90,15 +90,15 @@ const App = () => { } /> } /> - } /> - } /> + } /> + } /> - } /> - } /> + } /> + } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> @@ -112,15 +112,15 @@ const App = () => { } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> @@ -129,7 +129,7 @@ const App = () => { } /> - } /> + } /> } /> diff --git a/src/components/account-bar/account-bar.tsx b/src/components/account-bar/account-bar.tsx index 7bb6e6175..62d206b87 100644 --- a/src/components/account-bar/account-bar.tsx +++ b/src/components/account-bar/account-bar.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { createAccount, setActiveAccount, useAccount, useAccounts } from '@bitsocialnet/bitsocial-react-hooks'; +import { createAccount, setActiveAccount, useAccount, useAccounts } from '@bitsocial/bitsocial-react-hooks'; import { isSettingsView } from '../../lib/utils/view-utils'; import styles from './account-bar.module.css'; import SearchBar from '../search-bar'; diff --git a/src/components/author-sidebar/author-sidebar.tsx b/src/components/author-sidebar/author-sidebar.tsx index 15e40cbc3..de13a2134 100644 --- a/src/components/author-sidebar/author-sidebar.tsx +++ b/src/components/author-sidebar/author-sidebar.tsx @@ -3,43 +3,45 @@ import { Link, useLocation, useParams } from 'react-router-dom'; import { useAccount, useAccountComments, - useAccountSubplebbits, - AccountSubplebbit, + useAccountCommunities, + AccountCommunity, + Community, useAuthor, useAuthorAvatar, useAuthorComments, useBlock, useComment, - useSubplebbits, -} from '@bitsocialnet/bitsocial-react-hooks'; -import Plebbit from '@plebbit/plebbit-js'; + useCommunities, +} from '@bitsocial/bitsocial-react-hooks'; import styles from './author-sidebar.module.css'; import { getFormattedTimeDuration } from '../../lib/utils/time-utils'; import { getOldestAccountHistoryTimestamp } from '../../lib/utils/account-history-utils'; import { isAuthorView, isProfileView } from '../../lib/utils/view-utils'; -import { findAuthorSubplebbits, estimateAuthorKarma } from '../../lib/utils/user-utils'; +import { findAuthorCommunities, estimateAuthorKarma } from '../../lib/utils/user-utils'; +import getShortAddress from '../../lib/utils/address-utils'; +import { getCommunityIdentifiers } from '../../hooks/use-community-identifier'; import { useTranslation } from 'react-i18next'; import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; interface AuthorModeratingListProps { - accountSubplebbits: Record; - authorSubplebbits: string[]; + accountCommunities: Record>; + authorCommunities: string[]; isAuthor?: boolean; } -const AuthorModeratingList = ({ accountSubplebbits, authorSubplebbits, isAuthor = false }: AuthorModeratingListProps) => { +const AuthorModeratingList = ({ accountCommunities, authorCommunities, isAuthor = false }: AuthorModeratingListProps) => { const { t } = useTranslation(); - const rawAddresses = isAuthor ? authorSubplebbits : Object.keys(accountSubplebbits); - const subplebbitAddresses = [...new Set(rawAddresses)]; + const rawAddresses = isAuthor ? authorCommunities : Object.keys(accountCommunities); + const communityAddresses = [...new Set(rawAddresses)]; return ( - subplebbitAddresses.length > 0 && ( + communityAddresses.length > 0 && (
{t('moderator_of')}
    - {subplebbitAddresses.map((address, index) => ( + {communityAddresses.map((address, index) => (
  • - s/{Plebbit.getShortAddress({ address })} + s/{getShortAddress(address)}
  • ))}
@@ -65,25 +67,25 @@ const AuthorSidebar = () => { const userAccount = useAccount(); const { imageUrl: profilePageAvatar } = useAuthorAvatar({ author: userAccount?.author }); const { accountComments: oldestAccountComment } = useAccountComments({ page: 0, pageSize: 1, order: 'asc' }); - const { accountSubplebbits } = useAccountSubplebbits(); + const { accountCommunities } = useAccountCommunities(); const profileOldestAccountTimestamp = getOldestAccountHistoryTimestamp(oldestAccountComment as { timestamp?: number }[]); const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); const accountSubscriptions = userAccount?.subscriptions || []; const subscriptionsAndDefaults = [...accountSubscriptions, ...defaultSubplebbitAddresses]; - const subplebbits = - useSubplebbits({ - subplebbitAddresses: subscriptionsAndDefaults || [], + const communities = + useCommunities({ + communities: getCommunityIdentifiers(subscriptionsAndDefaults || []), onlyIfCached: true, - }).subplebbits?.filter(Boolean) || []; + }).communities?.filter(Boolean) || []; const authorAccount = useAuthor({ authorAddress, commentCid }); const { authorComments } = useAuthorComments({ authorAddress, commentCid }); const authorOldestCommentTimestamp = authorComments?.length ? Math.min(...authorComments.filter((comment): comment is NonNullable => comment != null).map((comment) => comment.timestamp)) : Date.now(); - const authorSubplebbits = findAuthorSubplebbits(authorAddress, Object.values(subplebbits)); + const authorCommunities = findAuthorCommunities(authorAddress, Object.values(communities)); const estimatedAuthorKarma = estimateAuthorKarma(authorComments); const address = isInAuthorView ? params?.authorAddress : isInProfileView ? userAccount?.author?.address : ''; @@ -166,8 +168,8 @@ const AuthorSidebar = () => { {t('user_since', { time: getFormattedTimeDuration(oldestCommentTimestamp) })}
- {(Object.keys(accountSubplebbits).length > 0 || authorSubplebbits.length > 0) && ( - + {(Object.keys(accountCommunities).length > 0 || authorCommunities.length > 0) && ( + )} ); diff --git a/src/components/challenge-modal/challenge-modal.tsx b/src/components/challenge-modal/challenge-modal.tsx index 18a8740ee..605bdc156 100644 --- a/src/components/challenge-modal/challenge-modal.tsx +++ b/src/components/challenge-modal/challenge-modal.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { FloatingFocusManager, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react'; -import { Challenge as ChallengeType, useComment, useAccount } from '@bitsocialnet/bitsocial-react-hooks'; +import { Challenge as ChallengeType, useComment, useAccount } from '@bitsocial/bitsocial-react-hooks'; import { useTranslation } from 'react-i18next'; import useChallengesStore from '../../stores/use-challenges-store'; import useTheme from '../../hooks/use-theme'; @@ -13,7 +13,7 @@ interface ChallengeHeaderProps { parentCid?: string; parentAddress?: string; publicationContent: string; - subplebbit?: string; + communityShortAddress?: string; } const useParentAddress = (parentCid?: string) => { @@ -21,12 +21,12 @@ const useParentAddress = (parentCid?: string) => { return parentComment?.author?.shortAddress; }; -const ChallengeHeader = ({ publicationType, votePreview, parentCid, parentAddress, publicationContent, subplebbit }: ChallengeHeaderProps) => { +const ChallengeHeader = ({ publicationType, votePreview, parentCid, parentAddress, publicationContent, communityShortAddress }: ChallengeHeaderProps) => { const { t } = useTranslation(); return ( <> -
{t('challenge_from', { subplebbit })}
+
{t('challenge_from', { subplebbit: communityShortAddress })}
{publicationType === 'vote' && votePreview + ' '} {parentCid @@ -203,7 +203,7 @@ const RegularChallengeContent = ({ challenge, closeModal }: RegularChallengeCont return () => document.removeEventListener('keydown', onEscapeKey); }, [isIframeChallenge, onIframeClose, closeModal]); - const subplebbit = shortSubplebbitAddress || subplebbitAddress; + const communityShortAddress = shortSubplebbitAddress || subplebbitAddress; // Render iframe challenge if (isIframeChallenge) { @@ -215,7 +215,7 @@ const RegularChallengeContent = ({ challenge, closeModal }: RegularChallengeCont parentCid={parentCid} parentAddress={parentAddress} publicationContent={publicationContent} - subplebbit={subplebbit} + communityShortAddress={communityShortAddress} /> {showIframeConfirmation ? ( @@ -223,9 +223,9 @@ const RegularChallengeContent = ({ challenge, closeModal }: RegularChallengeCont
{t('iframe_challenge_open_confirmation', { - subplebbit, + subplebbit: communityShortAddress, url: decodeURIComponent(getChallengeUrl()), - defaultValue: `s/${subplebbit} challenge wants to open ${decodeURIComponent(getChallengeUrl())}`, + defaultValue: `s/${communityShortAddress} challenge wants to open ${decodeURIComponent(getChallengeUrl())}`, })}
@@ -271,7 +271,7 @@ const RegularChallengeContent = ({ challenge, closeModal }: RegularChallengeCont parentCid={parentCid} parentAddress={parentAddress} publicationContent={publicationContent} - subplebbit={subplebbit} + communityShortAddress={communityShortAddress} />
{isTextChallenge &&
{currentChallenge?.challenge}
} diff --git a/src/components/comment-edit-form/comment-edit-form.tsx b/src/components/comment-edit-form/comment-edit-form.tsx index 819194475..087fd8d80 100644 --- a/src/components/comment-edit-form/comment-edit-form.tsx +++ b/src/components/comment-edit-form/comment-edit-form.tsx @@ -1,9 +1,10 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@bitsocialnet/bitsocial-react-hooks'; +import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@bitsocial/bitsocial-react-hooks'; import { FormattingHelpTable } from '../reply-form'; import styles from '../reply-form/reply-form.module.css'; import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils'; +import { getCommentCommunityAddress } from '../../lib/utils/comment-utils'; import challengesStore from '../../stores/use-challenges-store'; import Markdown from '../markdown'; @@ -31,7 +32,8 @@ const CommentEditForm = ({ commentCid, hideCommentEditForm }: CommentEditFormPro post = comment; } - const { content, reason, spoiler, nsfw, subplebbitAddress } = post || {}; + const { content, reason, spoiler, nsfw } = post || {}; + const communityAddress = getCommentCommunityAddress(post); const defaultPublishOptions: PublishCommentEditOptions = { commentCid, @@ -39,7 +41,7 @@ const CommentEditForm = ({ commentCid, hideCommentEditForm }: CommentEditFormPro reason, spoiler, nsfw, - subplebbitAddress, + subplebbitAddress: communityAddress, onChallenge: (...args: any) => addChallenge([...args, post]), onChallengeVerification: alertChallengeVerificationFailed, onError: (error: Error) => { diff --git a/src/components/feed-footer/feed-footer.tsx b/src/components/feed-footer/feed-footer.tsx index 41c31296b..56bba6a1b 100644 --- a/src/components/feed-footer/feed-footer.tsx +++ b/src/components/feed-footer/feed-footer.tsx @@ -11,8 +11,8 @@ interface FeedFooterProps { feedLength: number; hasFeedLoaded: boolean; hasMore: boolean; - subplebbitAddresses: string[]; - subplebbitAddressesWithNewerPosts: string[]; + communityAddresses: string[]; + communityAddressesWithNewerPosts: string[]; weeklyFeedLength: number; monthlyFeedLength: number; yearlyFeedLength: number; @@ -28,7 +28,7 @@ const FeedFooter = ({ feedLength, hasFeedLoaded, hasMore, - subplebbitAddresses, + communityAddresses, weeklyFeedLength, monthlyFeedLength, yearlyFeedLength, @@ -45,15 +45,15 @@ const FeedFooter = ({ const isInModView = isModView(location.pathname); const isInAllView = isAllView(location.pathname); - const feedStateString = useFeedStateString(subplebbitAddresses); + const feedStateString = useFeedStateString(communityAddresses); const loadingStateString = - useFeedStateString(subplebbitAddresses) || + useFeedStateString(communityAddresses) || (!hasFeedLoaded || (feedLength === 0 && !(weeklyFeedLength > feedLength || monthlyFeedLength > feedLength || yearlyFeedLength > feedLength)) ? t('loading_feed') : t('looking_for_more_posts')); // Add state to track initial loading - const [hasFetchedSubplebbitAddresses, setHasFetchedSubplebbitAddresses] = useState(false); + const [hasFetchedCommunityAddresses, setHasFetchedSubplebbitAddresses] = useState(false); // Set hasInitialized after a short delay useEffect(() => { @@ -63,7 +63,7 @@ const FeedFooter = ({ return () => clearTimeout(timer); }, []); - if (!hasFetchedSubplebbitAddresses) { + if (!hasFetchedCommunityAddresses) { footerContent = ; } @@ -110,7 +110,7 @@ const FeedFooter = ({ !(weeklyFeedLength > feedLength || monthlyFeedLength > feedLength || yearlyFeedLength > feedLength) ) { footerContent = t('no_posts'); - } else if (hasMore || subplebbitAddresses.length > 0 || (subplebbitAddresses && subplebbitAddresses.length === 0)) { + } else if (hasMore || communityAddresses.length > 0 || (communityAddresses && communityAddresses.length === 0)) { // Only show newer posts/weekly/monthly suggestions when not searching footerContent = ( <> @@ -146,7 +146,7 @@ const FeedFooter = ({
) : null}
- {subplebbitAddresses.length === 0 ? ( + {communityAddresses.length === 0 ? ( isInModView ? (
{t('not_moderator')}
) : ( diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index 024f54763..89da20184 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,7 +1,6 @@ import { Link, useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useAccount, useAccountComment, useSubplebbit } from '@bitsocialnet/bitsocial-react-hooks'; -import Plebbit from '@plebbit/plebbit-js'; +import { useAccount, useAccountComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import { sortTypes } from '../../constants/sort-types'; import { sortLabels } from '../../constants/sort-labels'; import { @@ -11,7 +10,7 @@ import { isAuthorView, isAuthorCommentsView, isAuthorSubmittedView, - isCreateSubplebbitView, + isCreateCommunityView, isHomeAboutView, isHomeView, isInboxView, @@ -25,28 +24,30 @@ import { isProfileHiddenView, isSettingsView, isSubmitView, - isSubplebbitView, - isSubplebbitSettingsView, - isSubplebbitSubmitView, - isSubplebbitsView, - isSubplebbitsSubscriberView, - isSubplebbitsModeratorView, - isSubplebbitsAdminView, - isSubplebbitsVoteView, - isSubplebbitsOwnerView, + isCommunityView, + isCommunitySettingsView, + isCommunitySubmitView, + isCommunitiesView, + isCommunitiesSubscriberView, + isCommunitiesModeratorView, + isCommunitiesAdminView, + isCommunitiesVoteView, + isCommunitiesOwnerView, isProfileUpvotedView, isSettingsContentOptionsView, isSettingsPlebbitOptionsView, - isSubplebbitAboutView, + isCommunityAboutView, isDomainView, isPostPageAboutView, isSettingsAccountDataView, } from '../../lib/utils/view-utils'; +import getShortAddress from '../../lib/utils/address-utils'; import useContentOptionsStore from '../../stores/use-content-options-store'; import useNotFoundStore from '../../stores/use-not-found-store'; -import { useIsBroadlyNsfwSubplebbit } from '../../hooks/use-is-broadly-nsfw-subplebbit'; +import { useIsBroadlyNsfwCommunity } from '../../hooks/use-is-broadly-nsfw-community'; import useTheme from '../../hooks/use-theme'; import useWindowWidth from '../../hooks/use-window-width'; +import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; import styles from './header.module.css'; const AboutButton = () => { @@ -56,10 +57,10 @@ const AboutButton = () => { const aboutLink = getAboutLink(location.pathname, params); const isInHomeAboutView = isHomeAboutView(location.pathname); const isInPostPageAboutView = isPostPageAboutView(location.pathname, params); - const isInSubplebbitAboutView = isSubplebbitAboutView(location.pathname, params); + const isInCommunityAboutView = isCommunityAboutView(location.pathname, params); return ( -
  • +
  • {t('about')}
  • ); @@ -76,7 +77,7 @@ const CommentsButton = () => { return (
  • - isInPendingPostView && e.preventDefault()}> + isInPendingPostView && e.preventDefault()}> {t('comments')}
  • @@ -89,18 +90,18 @@ const SortItems = () => { const location = useLocation(); const isInHomeAboutView = isHomeAboutView(location.pathname); const isInPostPageAboutView = isPostPageAboutView(location.pathname, params); - const isInSubplebbitAboutView = isSubplebbitAboutView(location.pathname, params); + const isInCommunityAboutView = isCommunityAboutView(location.pathname, params); const isInAllView = isAllView(location.pathname); const isInModView = isModView(location.pathname); const isInDomainView = isDomainView(location.pathname); - const isInSubplebbitView = isSubplebbitView(location.pathname, params); + const isInCommunityView = isCommunityView(location.pathname, params); // Derive selection directly from route instead of syncing via an effect - const selectedSortType = isInHomeAboutView || isInSubplebbitAboutView || isInPostPageAboutView ? '' : params.sortType || 'hot'; + const selectedSortType = isInHomeAboutView || isInCommunityAboutView || isInPostPageAboutView ? '' : params.sortType || 'hot'; const timeFilterName = params.timeFilterName; return sortTypes.map((sortType, index) => { - let sortLink = isInSubplebbitView - ? `/s/${params.subplebbitAddress}/${sortType}` + let sortLink = isInCommunityView + ? `/s/${params.communityAddress}/${sortType}` : isInAllView ? `/s/all/${sortType}` : isInModView @@ -193,30 +194,30 @@ const InboxHeaderTabs = () => { ); }; -const SubplebbitsHeaderTabs = () => { +const CommunitiesHeaderTabs = () => { const { t } = useTranslation(); const location = useLocation(); - const isInSubplebbitsSubscriberView = isSubplebbitsSubscriberView(location.pathname); - const isInSubplebbitsModeratorView = isSubplebbitsModeratorView(location.pathname); - const isInSubplebbitsAdminView = isSubplebbitsAdminView(location.pathname); - const isInSubplebbitsOwnerView = isSubplebbitsOwnerView(location.pathname); - const isInSubplebbitsVoteView = isSubplebbitsVoteView(location.pathname); - const isInSubplebbitsView = - isSubplebbitsView(location.pathname) && - !isInSubplebbitsSubscriberView && - !isInSubplebbitsModeratorView && - !isInSubplebbitsAdminView && - !isInSubplebbitsOwnerView && - !isInSubplebbitsVoteView; + const isInCommunitiesSubscriberView = isCommunitiesSubscriberView(location.pathname); + const isInCommunitiesModeratorView = isCommunitiesModeratorView(location.pathname); + const isInCommunitiesAdminView = isCommunitiesAdminView(location.pathname); + const isInCommunitiesOwnerView = isCommunitiesOwnerView(location.pathname); + const isInCommunitiesVoteView = isCommunitiesVoteView(location.pathname); + const isInCommunitiesView = + isCommunitiesView(location.pathname) && + !isInCommunitiesSubscriberView && + !isInCommunitiesModeratorView && + !isInCommunitiesAdminView && + !isInCommunitiesOwnerView && + !isInCommunitiesVoteView; return ( <> -
  • +
  • {t('vote')}
  • { const isInPendingPostView = isPendingPostView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params); const isInProfileView = isProfileView(location.pathname); - const isInSubplebbitView = isSubplebbitView(location.pathname, params); - const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params); - const isInSubplebbitSubmitView = isSubplebbitSubmitView(location.pathname, params); - const isInSubplebbitsView = isSubplebbitsView(location.pathname); - const isInCreateSubplebbitView = isCreateSubplebbitView(location.pathname); + const isInCommunityView = isCommunityView(location.pathname, params); + const isInCommunitySettingsView = isCommunitySettingsView(location.pathname, params); + const isInCommunitySubmitView = isCommunitySubmitView(location.pathname, params); + const isInCommunitiesView = isCommunitiesView(location.pathname); + const isInCreateCommunityView = isCreateCommunityView(location.pathname); const isInSettingsView = isSettingsView(location.pathname); const isInSettingsContentOptionsView = isSettingsContentOptionsView(location.pathname); const isInSettingsPlebbitOptionsView = isSettingsPlebbitOptionsView(location.pathname); @@ -278,7 +279,7 @@ const HeaderTabs = () => { isInHomeView || isInHomeAboutView || isInPostPageAboutView || - (isInSubplebbitView && !isInSubplebbitSubmitView && !isInSubplebbitSettingsView) || + (isInCommunityView && !isInCommunitySubmitView && !isInCommunitySettingsView) || isInAllView || isInModView || isInDomainView @@ -288,15 +289,15 @@ const HeaderTabs = () => { return ; } else if (isInInboxView) { return ; - } else if (isInSubplebbitsView && !isInCreateSubplebbitView) { - return ; + } else if (isInCommunitiesView && !isInCreateCommunityView) { + return ; } else if (isInSettingsView || isInSettingsPlebbitOptionsView || isInSettingsContentOptionsView) { return ; } return null; }; -const HeaderTitle = ({ title, pendingPostSubplebbitAddress }: { title: string; pendingPostSubplebbitAddress?: string }) => { +const HeaderTitle = ({ title, pendingPostCommunityAddress }: { title: string; pendingPostCommunityAddress?: string }) => { const account = useAccount(); const { t } = useTranslation(); const params = useParams(); @@ -313,45 +314,41 @@ const HeaderTitle = ({ title, pendingPostSubplebbitAddress }: { title: string; p const isInSettingsContentOptionsView = isSettingsContentOptionsView(location.pathname); const isInSettingsPlebbitOptionsView = isSettingsPlebbitOptionsView(location.pathname); const isInSubmitView = isSubmitView(location.pathname); - const isInSubplebbitView = isSubplebbitView(location.pathname, params); - const isInSubplebbitSubmitView = isSubplebbitSubmitView(location.pathname, params); - const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params); - const isInSubplebbitsView = isSubplebbitsView(location.pathname); - const isInCreateSubplebbitView = isCreateSubplebbitView(location.pathname); + const isInCommunityView = isCommunityView(location.pathname, params); + const isInCommunitySubmitView = isCommunitySubmitView(location.pathname, params); + const isInCommunitySettingsView = isCommunitySettingsView(location.pathname, params); + const isInCommunitiesView = isCommunitiesView(location.pathname); + const isInCreateCommunityView = isCreateCommunityView(location.pathname); const isInNotFoundView = useNotFoundStore((state) => state.isNotFound); - const subplebbitAddress = params.subplebbitAddress; + const communityAddress = params.communityAddress; const { hideAdultCommunities, hideGoreCommunities, hideAntiCommunities, hideVulgarCommunities } = useContentOptionsStore(); const hasUnhiddenAnyNsfwCommunity = !hideAdultCommunities || !hideGoreCommunities || !hideAntiCommunities || !hideVulgarCommunities; - const isBroadlyNsfwSubplebbit = useIsBroadlyNsfwSubplebbit(subplebbitAddress || ''); + const isBroadlyNsfwCommunity = useIsBroadlyNsfwCommunity(communityAddress || ''); - const subplebbitTitle = ( - - {title || - (subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress })) || - (pendingPostSubplebbitAddress && Plebbit.getShortAddress({ address: pendingPostSubplebbitAddress }))} + const communityTitle = ( + + {title || (communityAddress && getShortAddress(communityAddress)) || (pendingPostCommunityAddress && getShortAddress(pendingPostCommunityAddress))} ); const domainTitle = {params.domain}; const submitTitle = {t('submit')}; const profileTitle = {account?.author?.shortAddress}; - const authorTitle = ( - {params.authorAddress && Plebbit.getShortAddress({ address: params.authorAddress })} - ); + const authorTitle = {params.authorAddress && getShortAddress(params.authorAddress)}; - if (isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity) { + if (isBroadlyNsfwCommunity && !hasUnhiddenAnyNsfwCommunity) { return {t('over_18')}; - } else if (isInSubplebbitSubmitView) { + } else if (isInCommunitySubmitView) { return ( <> - {subplebbitTitle}: {submitTitle} + {communityTitle}: {submitTitle} ); - } else if (isInSubplebbitSettingsView) { + } else if (isInCommunitySettingsView) { return ( <> - {subplebbitTitle}: {t('community_settings')} + {communityTitle}: {t('community_settings')} ); } else if (isInSubmitView) { @@ -360,15 +357,15 @@ const HeaderTitle = ({ title, pendingPostSubplebbitAddress }: { title: string; p return t('preferences'); } else if (isInProfileView && !isInPendingPostView) { return profileTitle; - } else if (isInPostPageView || isInPendingPostView || (isInSubplebbitView && !isInSubplebbitSettingsView)) { - return subplebbitTitle; + } else if (isInPostPageView || isInPendingPostView || (isInCommunityView && !isInCommunitySettingsView)) { + return communityTitle; } else if (isInAuthorView) { return authorTitle; } else if (isInInboxView) { return t('messages'); - } else if (isInCreateSubplebbitView) { + } else if (isInCreateCommunityView) { return {t('create_community')}; - } else if (isInSubplebbitsView) { + } else if (isInCommunitiesView) { return t('communities'); } else if (isInNotFoundView) { return {t('page_not_found')}; @@ -387,8 +384,8 @@ const Header = () => { const [theme] = useTheme(); const location = useLocation(); const params = useParams(); - const subplebbit = useSubplebbit({ subplebbitAddress: params?.subplebbitAddress, onlyIfCached: true }); - const { suggested, title } = subplebbit || {}; + const community = useCommunity(params?.communityAddress ? { community: getCommunityIdentifier(params.communityAddress), onlyIfCached: true } : undefined); + const { suggested, title } = community || {}; const commentIndex = params?.accountCommentIndex ? parseInt(params?.accountCommentIndex) : undefined; const accountComment = useAccountComment({ commentIndex }); @@ -407,24 +404,23 @@ const Header = () => { const isInPendingPostView = isPendingPostView(location.pathname, params); const isInProfileView = isProfileView(location.pathname); const isInSettingsView = isSettingsView(location.pathname); - const isInSubplebbitView = isSubplebbitView(location.pathname, params); - const isInSubplebbitAboutView = isSubplebbitAboutView(location.pathname, params); + const isInCommunityView = isCommunityView(location.pathname, params); + const isInCommunityAboutView = isCommunityAboutView(location.pathname, params); const isInSubmitView = isSubmitView(location.pathname); - const isInSubplebbitSubmitView = isSubplebbitSubmitView(location.pathname, params); - const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params); + const isInCommunitySubmitView = isCommunitySubmitView(location.pathname, params); + const isInCommunitySettingsView = isCommunitySettingsView(location.pathname, params); const isInNotFoundView = useNotFoundStore((state) => state.isNotFound); - const hasFewTabs = - isInPostPageView || isInSubmitView || isInSubplebbitSubmitView || isInSubplebbitSettingsView || isInSettingsView || isInInboxView || isInSettingsView; + const hasFewTabs = isInPostPageView || isInSubmitView || isInCommunitySubmitView || isInCommunitySettingsView || isInSettingsView || isInInboxView || isInSettingsView; const hasStickyHeader = isInHomeView || isInNotFoundView || - (isInSubplebbitView && - !isInSubplebbitSubmitView && - !isInSubplebbitSettingsView && + (isInCommunityView && + !isInCommunitySubmitView && + !isInCommunitySettingsView && !isInPostPageView && !isInHomeAboutView && - !isInSubplebbitAboutView && + !isInCommunityAboutView && !isInPostPageAboutView) || (isInProfileView && !isInHomeAboutView) || (isInAllView && !isInAllAboutView) || @@ -432,7 +428,7 @@ const Header = () => { (isInDomainView && !isInHomeAboutView) || (isInAuthorView && !isInHomeAboutView); - const subplebbitAddress = params.subplebbitAddress; + const communityAddress = params.communityAddress; const contentOptionsStore = useContentOptionsStore(); const hasUnhiddenAnyNsfwCommunity = @@ -440,9 +436,9 @@ const Header = () => { !contentOptionsStore.hideGoreCommunities || !contentOptionsStore.hideAntiCommunities || !contentOptionsStore.hideVulgarCommunities; - const isBroadlyNsfwSubplebbit = useIsBroadlyNsfwSubplebbit(subplebbitAddress || ''); + const isBroadlyNsfwCommunity = useIsBroadlyNsfwCommunity(communityAddress || ''); - const logoIsAvatar = isInSubplebbitView && suggested?.avatarUrl && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity); + const logoIsAvatar = isInCommunityView && suggested?.avatarUrl && !(isBroadlyNsfwCommunity && !hasUnhiddenAnyNsfwCommunity); const logoSrc = logoIsAvatar ? suggested?.avatarUrl : 'assets/sprout/sprout.png'; const logoLink = '/'; @@ -451,48 +447,48 @@ const Header = () => { ? '/submit' : isInPendingPostView ? `/s/${accountComment?.subplebbitAddress}/submit` - : subplebbitAddress - ? `/s/${subplebbitAddress}/submit` + : communityAddress + ? `/s/${communityAddress}/submit` : '/submit'; return (
    - {(logoIsAvatar || (!isInSubplebbitView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && ( + {(logoIsAvatar || (!isInCommunityView && !isInProfileView && !isInAuthorView)) && ( )} - {((!isInSubplebbitView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && ( + {((!isInCommunityView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && ( )}
    {!isInHomeView && !isInHomeAboutView && !isInModView && !isInAllView && ( - + )} {(isInModView || isInAllView) && (
    - +
    )} - {!isMobile && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity) && ( + {!isMobile && !(isBroadlyNsfwCommunity && !hasUnhiddenAnyNsfwCommunity) && (
      {(isInHomeView || isInHomeAboutView) && }
    )}
    - {isMobile && !isInSubplebbitSubmitView && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity) && ( + {isMobile && !isInCommunitySubmitView && !(isBroadlyNsfwCommunity && !hasUnhiddenAnyNsfwCommunity) && (
      - {(isInHomeView || isInHomeAboutView || isInSubplebbitView || isInHomeAboutView || isInPostPageView) && } + {(isInHomeView || isInHomeAboutView || isInCommunityView || isInHomeAboutView || isInPostPageView) && } {!isInSubmitView && !isInSettingsView && (
    • diff --git a/src/components/notification-handler/notification-handler.tsx b/src/components/notification-handler/notification-handler.tsx index 820387ff5..dfa58bd07 100644 --- a/src/components/notification-handler/notification-handler.tsx +++ b/src/components/notification-handler/notification-handler.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; import { useLocation } from 'react-router-dom'; -import { useNotifications } from '@bitsocialnet/bitsocial-react-hooks'; -import localForageLru from '@bitsocialnet/bitsocial-react-hooks/dist/lib/localforage-lru/index.js'; +import { useNotifications } from '@bitsocial/bitsocial-react-hooks'; +import localForageLru from '@bitsocial/bitsocial-react-hooks/dist/lib/localforage-lru/index.js'; import useContentOptionsStore from '../../stores/use-content-options-store'; import { showLocalNotification } from '../../lib/push'; diff --git a/src/components/post/comment-tools/comment-tools.tsx b/src/components/post/comment-tools/comment-tools.tsx index a98a9d87c..4b749cbe0 100644 --- a/src/components/post/comment-tools/comment-tools.tsx +++ b/src/components/post/comment-tools/comment-tools.tsx @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Author, useAccount, useComment, useSubplebbit } from '@bitsocialnet/bitsocial-react-hooks'; +import { Author, useAccount, useComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import useScheduledReset from '../../../hooks/use-scheduled-reset'; import styles from './comment-tools.module.css'; import EditMenu from './edit-menu'; @@ -9,6 +9,7 @@ import HideMenu from './hide-menu'; import Label from '../label'; import ModMenu from './mod-menu'; import { isInboxView } from '../../../lib/utils/view-utils'; +import { getCommunityIdentifier } from '../../../hooks/use-community-identifier'; import { copyShareLinkToClipboard } from '../../../lib/utils/url-utils'; interface CommentToolsProps { @@ -31,7 +32,7 @@ interface CommentToolsProps { removed?: boolean; replyCount?: number; spoiler?: boolean | undefined; - subplebbitAddress: string; + communityAddress: string; showCommentEditForm?: () => void; showReplyForm?: () => void; } @@ -57,7 +58,7 @@ const ModOrReportButton = ({ cid, isAuthor, isAccountMod, isCommentAuthorMod }: ); }; -const ShareButton = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress: string }) => { +const ShareButton = ({ cid, communityAddress }: { cid: string; communityAddress: string }) => { const { t } = useTranslation(); const [hasCopied, setHasCopied] = useState(false); @@ -68,7 +69,7 @@ const ShareButton = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddres try { setHasCopied(true); scheduleReset(); - await copyShareLinkToClipboard(subplebbitAddress, cid); + await copyShareLinkToClipboard(communityAddress, cid); } catch (error) { console.error('Failed to copy share link:', error); setHasCopied(false); @@ -92,7 +93,7 @@ const PostTools = ({ isAuthor, isAccountMod, isCommentAuthorMod, - subplebbitAddress, + communityAddress, replyCount = 0, showCommentEditForm, }: CommentToolsProps) => { @@ -114,7 +115,7 @@ const PostTools = ({ const commentCountButton = failed ? ( {commentCount} ) : ( - cid && handlePostClick?.()}> + cid && handlePostClick?.()}> {commentCount} ); @@ -122,14 +123,14 @@ const PostTools = ({ return ( <>
    • {commentCountButton}
    • - + {/* TODO: Implement save functionality
    • {t('save')}
    • */} {isAuthor && } - + {/* TODO: Implement crosspost functionality
    • {t('crosspost')} @@ -150,7 +151,7 @@ const ReplyTools = ({ isAccountMod, isCommentAuthorMod, showReplyForm, - subplebbitAddress, + communityAddress, showCommentEditForm, }: CommentToolsProps) => { const { t } = useTranslation(); @@ -158,7 +159,7 @@ const ReplyTools = ({ const permalink = failed ? ( permalink ) : ( - !cid && e.preventDefault()}> + !cid && e.preventDefault()}> permalink ); @@ -166,14 +167,14 @@ const ReplyTools = ({ return ( <>
    • {permalink}
    • - + {/* TODO: Implement save functionality
    • {t('save')}
    • */} {isAuthor && } - +
    • cid && showReplyForm?.()}>{t('reply_reply')}
    • @@ -193,7 +194,7 @@ const SingleReplyTools = ({ parentCid, postCid, showReplyForm, - subplebbitAddress, + communityAddress, showCommentEditForm, }: CommentToolsProps) => { const { t } = useTranslation(); @@ -202,7 +203,7 @@ const SingleReplyTools = ({ const hasContext = parentCid !== postCid; const permalinkButton = cid ? ( - !cid && e.preventDefault()}> + !cid && e.preventDefault()}> permalink ) : ( @@ -210,13 +211,13 @@ const SingleReplyTools = ({ ); const contextButton = cid ? ( - {t('context')} + {t('context')} ) : ( {t('context')} ); const fullCommentsButton = cid ? ( - + {t('full_comments')} {comment?.replyCount ? `(${comment?.replyCount})` : ''} ) : ( @@ -236,7 +237,7 @@ const SingleReplyTools = ({ {isAuthor && }
    • {contextButton}
    • {fullCommentsButton}
    • - +
    • cid && showReplyForm?.()}>{t('reply_reply')}
    • @@ -290,15 +291,15 @@ const CommentTools = ({ removed, replyCount, spoiler, - subplebbitAddress, + communityAddress, showCommentEditForm, showReplyForm, }: CommentToolsProps) => { const account = useAccount(); const isAuthor = account?.author?.address === author?.address; - const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true }); - const accountAuthorRole = subplebbit?.roles?.[account?.author?.address]?.role; - const commentAuthorRole = subplebbit?.roles?.[author?.address]?.role; + const community = useCommunity(communityAddress ? { community: getCommunityIdentifier(communityAddress), onlyIfCached: true } : undefined); + const accountAuthorRole = community?.roles?.[account?.author?.address]?.role; + const commentAuthorRole = community?.roles?.[author?.address]?.role; const isAccountMod = accountAuthorRole === 'admin' || accountAuthorRole === 'owner' || accountAuthorRole === 'moderator'; const isCommentAuthorMod = commentAuthorRole === 'admin' || commentAuthorRole === 'owner' || commentAuthorRole === 'moderator'; const isInInboxView = isInboxView(useLocation().pathname); @@ -322,7 +323,7 @@ const CommentTools = ({ postCid={postCid} showCommentEditForm={showCommentEditForm} showReplyForm={showReplyForm} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress} /> ) : ( ) ) : ( @@ -351,7 +352,7 @@ const CommentTools = ({ nsfw={nsfw} removed={removed} spoiler={spoiler} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress} /> )} diff --git a/src/components/post/comment-tools/edit-menu/edit-menu.tsx b/src/components/post/comment-tools/edit-menu/edit-menu.tsx index bcbf1b983..58550f124 100644 --- a/src/components/post/comment-tools/edit-menu/edit-menu.tsx +++ b/src/components/post/comment-tools/edit-menu/edit-menu.tsx @@ -1,8 +1,9 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@bitsocialnet/bitsocial-react-hooks'; +import { PublishCommentEditOptions, useComment, useEditedComment, usePublishCommentEdit } from '@bitsocial/bitsocial-react-hooks'; import styles from './edit-menu.module.css'; import { alertChallengeVerificationFailed } from '../../../../lib/utils/challenge-utils'; +import { getCommentCommunityAddress } from '../../../../lib/utils/comment-utils'; import challengesStore from '../../../../stores/use-challenges-store'; const { addChallenge } = challengesStore.getState(); @@ -24,12 +25,13 @@ const EditMenu = ({ commentCid, showCommentEditForm }: EditMenuProps) => { post = comment; } - const { deleted, subplebbitAddress } = post || {}; + const { deleted } = post || {}; + const communityAddress = getCommentCommunityAddress(post); const defaultPublishOptions: PublishCommentEditOptions = { commentCid, deleted, - subplebbitAddress, + subplebbitAddress: communityAddress, onChallenge: (...args: any) => addChallenge([...args, post]), onChallengeVerification: alertChallengeVerificationFailed, onError: (error: Error) => { diff --git a/src/components/post/comment-tools/hide-menu/hide-menu.tsx b/src/components/post/comment-tools/hide-menu/hide-menu.tsx index 8a0dfe0d8..58778d489 100644 --- a/src/components/post/comment-tools/hide-menu/hide-menu.tsx +++ b/src/components/post/comment-tools/hide-menu/hide-menu.tsx @@ -1,10 +1,10 @@ import { useState } from 'react'; import { useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Author, useBlock } from '@bitsocialnet/bitsocial-react-hooks'; -import Plebbit from '@plebbit/plebbit-js'; +import { Author, useBlock } from '@bitsocial/bitsocial-react-hooks'; import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react'; import { isProfileHiddenView } from '../../../../lib/utils/view-utils'; +import getShortAddress from '../../../../lib/utils/address-utils'; import styles from './hide-menu.module.css'; type HideMenuProps = { @@ -12,7 +12,7 @@ type HideMenuProps = { cid?: string; isAccountMod?: boolean; toggleIsMenuOpen?: () => void; - subplebbitAddress?: string; + communityAddress?: string; }; const BlockAuthorButton = ({ author, toggleIsMenuOpen }: HideMenuProps) => { @@ -32,13 +32,13 @@ const BlockAuthorButton = ({ author, toggleIsMenuOpen }: HideMenuProps) => { ); }; -const BlockSubplebbitButton = ({ subplebbitAddress }: HideMenuProps) => { +const BlockCommunityButton = ({ communityAddress }: HideMenuProps) => { const { t } = useTranslation(); - const { blocked, unblock, block } = useBlock({ address: subplebbitAddress }); + const { blocked, unblock, block } = useBlock({ address: communityAddress }); return (
      - {blocked ? `${t('unblock')}` : `${t('block')}`} s/{subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress })} + {blocked ? `${t('unblock')}` : `${t('block')}`} s/{communityAddress && getShortAddress(communityAddress)}
      ); }; @@ -54,7 +54,7 @@ const BlockCommentButton = ({ cid }: HideMenuProps) => { ); }; -const HideMenu = ({ author, cid, isAccountMod, subplebbitAddress }: HideMenuProps) => { +const HideMenu = ({ author, cid, isAccountMod, communityAddress }: HideMenuProps) => { const { t } = useTranslation(); const [isHideMenuOpen, setIsHideMenuOpen] = useState(false); const toggleIsMenuOpen = () => setIsHideMenuOpen(!isHideMenuOpen); @@ -88,7 +88,7 @@ const HideMenu = ({ author, cid, isAccountMod, subplebbitAddress }: HideMenuProp
      - + {!isAccountMod &&
      {t('report')}
      }
      diff --git a/src/components/post/comment-tools/mod-menu/mod-menu.tsx b/src/components/post/comment-tools/mod-menu/mod-menu.tsx index 66b29d868..259eacdea 100644 --- a/src/components/post/comment-tools/mod-menu/mod-menu.tsx +++ b/src/components/post/comment-tools/mod-menu/mod-menu.tsx @@ -1,9 +1,10 @@ import { useState } from 'react'; import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react'; import { Trans, useTranslation } from 'react-i18next'; -import { PublishCommentModerationOptions, useComment, useEditedComment, usePublishCommentModeration } from '@bitsocialnet/bitsocial-react-hooks'; +import { PublishCommentModerationOptions, useComment, useEditedComment, usePublishCommentModeration } from '@bitsocial/bitsocial-react-hooks'; import styles from './mod-menu.module.css'; import { alertChallengeVerificationFailed } from '../../../../lib/utils/challenge-utils'; +import { getCommentCommunityAddress } from '../../../../lib/utils/comment-utils'; import challengesStore from '../../../../stores/use-challenges-store'; const { addChallenge } = challengesStore.getState(); @@ -26,11 +27,12 @@ const ModMenu = ({ cid, isCommentAuthorMod }: ModMenuProps) => { const isReply = post?.parentCid; const [isModMenuOpen, setIsModMenuOpen] = useState(false); - const { removed, locked, spoiler, nsfw, pinned, banExpiresAt, subplebbitAddress, purged } = post || {}; + const { removed, locked, spoiler, nsfw, pinned, banExpiresAt, purged } = post || {}; + const communityAddress = getCommentCommunityAddress(post); const defaultPublishOptions: PublishCommentModerationOptions = { commentCid: cid, - subplebbitAddress, + subplebbitAddress: communityAddress, commentModeration: { removed: removed ?? false, purged: purged ?? false, @@ -118,7 +120,7 @@ const ModMenu = ({ cid, isCommentAuthorMod }: ModMenuProps) => { if (publishCommentModerationOptions.commentModeration.purged) { const confirmed = window.confirm( 'You are purging this comment. Are you sure you want to continue?\n\n' + - "The comment will be completely removed from this subplebbit's database. This action is irreversible.", + "The comment will be completely removed from this community's database. This action is irreversible.", ); if (!confirmed) { return; diff --git a/src/components/post/expando/expando.tsx b/src/components/post/expando/expando.tsx index c52e876e5..397a508a0 100644 --- a/src/components/post/expando/expando.tsx +++ b/src/components/post/expando/expando.tsx @@ -3,7 +3,7 @@ import { Link, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { CommentMediaInfo } from '../../../lib/utils/media-utils'; import useContentOptionsStore from '../../../stores/use-content-options-store'; -import { useIsNsfwSubplebbit } from '../../../hooks/use-is-nsfw-subplebbit'; +import { useIsNsfwCommunity } from '../../../hooks/use-is-nsfw-community'; import Markdown from '../../markdown'; import Embed from '../embed'; import styles from './expando.module.css'; @@ -79,14 +79,14 @@ const Expando = ({ mediaComponent = ; } - const pageSubplebbitAddress = useParams().subplebbitAddress; - const isNsfwSubplebbit = useIsNsfwSubplebbit(pageSubplebbitAddress || ''); + const pageCommunityAddress = useParams().communityAddress; + const isNsfwCommunity = useIsNsfwCommunity(pageCommunityAddress || ''); return (
      {link && !removed && commentMediaInfo?.type !== 'webpage' && (
      setHideContent(false)}> - {((nsfw && blurNsfwThumbnails && !isNsfwSubplebbit) || spoiler) && hideContent && link && commentMediaInfo?.type !== 'webpage' && !(deleted || removed) && ( + {((nsfw && blurNsfwThumbnails && !isNsfwCommunity) || spoiler) && hideContent && link && commentMediaInfo?.type !== 'webpage' && !(deleted || removed) && ( <>
      {nsfw && spoiler ? t('see_nsfw_spoiler') : spoiler ? t('view_spoiler') : nsfw ? t('see_nsfw') : ''} diff --git a/src/components/post/post.module.css b/src/components/post/post.module.css index f0cda68f6..417d5cf41 100644 --- a/src/components/post/post.module.css +++ b/src/components/post/post.module.css @@ -151,7 +151,7 @@ font-style: italic; } -.subplebbit:hover, .author:hover span { +.community:hover, .author:hover span { text-decoration: underline; } @@ -258,11 +258,11 @@ background-image: url("/assets/buttons/all_feed_subscribed.png"); } -.greenSubplebbitAddress { +.greenCommunityAddress { color: var(--green-bright) !important; } -.subscribeButtonWrapper:hover + .subplebbit { +.subscribeButtonWrapper:hover + .community { color: var(--green-bright) !important; } diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx index d40fb1fa0..67d5499a6 100644 --- a/src/components/post/post.tsx +++ b/src/components/post/post.tsx @@ -1,19 +1,21 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useLocation, useParams, useSearchParams } from 'react-router-dom'; -import { Comment, useAuthorAddress, useBlock, useComment, useEditedComment, useSubplebbit, useSubscribe } from '@bitsocialnet/bitsocial-react-hooks'; -import Plebbit from '@plebbit/plebbit-js'; +import { Comment, useAuthorAddress, useBlock, useComment, useEditedComment, useCommunity, useSubscribe } from '@bitsocial/bitsocial-react-hooks'; import { getHasThumbnail } from '../../lib/utils/media-utils'; +import getShortAddress from '../../lib/utils/address-utils'; import { getPostScore, formatScore } from '../../lib/utils/post-utils'; import { getFormattedTimeAgo, formatLocalizedUTCTimestamp } from '../../lib/utils/time-utils'; import { getHostname } from '../../lib/utils/url-utils'; -import { isAllView, isAuthorView, isPendingPostView, isPostPageView, isProfileHiddenView, isProfileView, isSubplebbitView } from '../../lib/utils/view-utils'; +import { isAllView, isAuthorView, isPendingPostView, isPostPageView, isProfileHiddenView, isProfileView, isCommunityView } from '../../lib/utils/view-utils'; import { highlightMatchedText } from '../../lib/utils/pattern-utils'; import { usePinnedPostsStore } from '../../stores/use-pinned-posts-store'; import { useCommentMediaInfo } from '../../hooks/use-comment-media-info'; import useDownvote from '../../hooks/use-downvote'; import useIsMobile from '../../hooks/use-is-mobile'; -import { useIsNsfwSubplebbit } from '../../hooks/use-is-nsfw-subplebbit'; +import { useIsNsfwCommunity } from '../../hooks/use-is-nsfw-community'; +import { getCommentCommunityAddress } from '../../lib/utils/comment-utils'; +import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; import useUpvote from '../../hooks/use-upvote'; import useWindowWidth from '../../hooks/use-window-width'; import CommentEditForm from '../comment-edit-form'; @@ -112,18 +114,18 @@ const Post = ({ index, post = {} }: PostProps) => { replyCount, spoiler, state, - subplebbitAddress, timestamp, title, upvoteCount, } = post || {}; + const communityAddress = getCommentCommunityAddress(post); const [searchParams] = useSearchParams(); const searchQuery = searchParams.get('q') || ''; - // Check if the subplebbit is NSFW based on its tags - const isNsfwSubplebbit = useIsNsfwSubplebbit(subplebbitAddress); - const nsfw = post?.nsfw || isNsfwSubplebbit; + // Check if the community is NSFW based on its tags + const isNsfwCommunity = useIsNsfwCommunity(communityAddress || ''); + const nsfw = post?.nsfw || isNsfwCommunity; const { displayName, shortAddress } = author || {}; const { shortAuthorAddress, authorAddressChanged } = useAuthorAddress({ comment: post }); @@ -133,9 +135,9 @@ const Post = ({ index, post = {} }: PostProps) => { const postDate = formatLocalizedUTCTimestamp(timestamp, language); const params = useParams(); const location = useLocation(); - const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true }); + const community = useCommunity(communityAddress ? { community: getCommunityIdentifier(communityAddress), onlyIfCached: true } : undefined); - const authorRole = subplebbit?.roles?.[post.author?.address]?.role; + const authorRole = community?.roles?.[post.author?.address]?.role; const isInAllView = isAllView(location.pathname); const isInPendingPostView = isPendingPostView(location.pathname, params); @@ -143,7 +145,7 @@ const Post = ({ index, post = {} }: PostProps) => { const isInProfileView = isProfileView(location.pathname); const isInAuthorView = isAuthorView(location.pathname); const isInProfileHiddenView = isProfileHiddenView(location.pathname); - const isInSubplebbitView = isSubplebbitView(location.pathname, params); + const isInCommunityView = isCommunityView(location.pathname, params); const commentMediaInfo = useCommentMediaInfo(post); @@ -176,7 +178,7 @@ const Post = ({ index, post = {} }: PostProps) => { const { blocked, unblock } = useBlock({ cid }); const [hasClickedSubscribe, setHasClickedSubscribe] = useState(false); - const { subscribe, subscribed } = useSubscribe({ subplebbitAddress }); + const { subscribe, subscribed } = useSubscribe({ communityAddress }); // show gray dotted border around last clicked post const isLastClicked = sessionStorage.getItem('lastClickedPost') === cid && !isInPostPageView; @@ -194,7 +196,7 @@ const Post = ({ index, post = {} }: PostProps) => { const windowWidth = useWindowWidth(); const pinnedPostsCount = usePinnedPostsStore((state) => state.pinnedPostsCount); let rank = (index ?? 0) + 1; - if (isInSubplebbitView) { + if (isInCommunityView) { rank = rank - pinnedPostsCount; } @@ -232,7 +234,7 @@ const Post = ({ index, post = {} }: PostProps) => { link={link} linkHeight={linkHeight} linkWidth={linkWidth} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress} isPdf={commentMediaInfo?.type === 'pdf'} /> )} @@ -245,7 +247,7 @@ const Post = ({ index, post = {} }: PostProps) => { {displayedTitle} ) : ( - + {displayedTitle} )} @@ -260,9 +262,7 @@ const Post = ({ index, post = {} }: PostProps) => { {hostname ? ( {hostname.length > 25 ? hostname.slice(0, 25) + '...' : hostname} ) : ( - - self.{subplebbit?.shortAddress || (subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress }))} - + self.{community?.shortAddress || (communityAddress && getShortAddress(communityAddress))} )} ) @@ -293,7 +293,7 @@ const Post = ({ index, post = {} }: PostProps) => { shortAuthorAddress={shortAuthorAddress} authorAddressChanged={authorAddressChanged} /> - {!isInSubplebbitView && ( + {!isInCommunityView && ( <>  {t('post_to')} @@ -308,8 +308,8 @@ const Post = ({ index, post = {} }: PostProps) => { /> )} - - s/{subplebbit?.shortAddress || (subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress }))} + + s/{community?.shortAddress || (communityAddress && getShortAddress(communityAddress))} @@ -328,7 +328,7 @@ const Post = ({ index, post = {} }: PostProps) => { replyCount={replyCount} showCommentEditForm={showCommentEditForm} spoiler={spoiler} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress || ''} />
      {!(windowWidth < 770) && !(!content && !link) && ( diff --git a/src/components/post/thumbnail/thumbnail.tsx b/src/components/post/thumbnail/thumbnail.tsx index a14fb7de1..b2052f3a1 100644 --- a/src/components/post/thumbnail/thumbnail.tsx +++ b/src/components/post/thumbnail/thumbnail.tsx @@ -4,7 +4,7 @@ import { Link, useParams } from 'react-router-dom'; import { CommentMediaInfo } from '../../../lib/utils/media-utils'; import useFetchGifFirstFrame from '../../../hooks/use-fetch-gif-first-frame'; import useContentOptionsStore from '../../../stores/use-content-options-store'; -import { useIsNsfwSubplebbit } from '../../../hooks/use-is-nsfw-subplebbit'; +import { useIsNsfwCommunity } from '../../../hooks/use-is-nsfw-community'; interface ThumbnailProps { cid?: string; @@ -18,7 +18,7 @@ interface ThumbnailProps { link: string; linkHeight?: number; linkWidth?: number; - subplebbitAddress?: string; + communityAddress?: string; toggleExpanded?: () => void; isPdf?: boolean; } @@ -35,7 +35,7 @@ const Thumbnail = ({ link, linkHeight, linkWidth, - subplebbitAddress, + communityAddress, toggleExpanded, isPdf = false, }: ThumbnailProps) => { @@ -45,8 +45,8 @@ const Thumbnail = ({ const thumbnailClass = expanded ? styles.thumbnailHidden : styles.thumbnailVisible; const { blurNsfwThumbnails } = useContentOptionsStore(); - const pageSubplebbitAddress = useParams().subplebbitAddress; - const isNsfwSubplebbit = useIsNsfwSubplebbit(pageSubplebbitAddress || ''); + const pageCommunityAddress = useParams().communityAddress; + const isNsfwCommunity = useIsNsfwCommunity(pageCommunityAddress || ''); if (linkWidth && linkHeight) { let scale = Math.min(1, 70 / Math.max(linkWidth, linkHeight)); @@ -59,7 +59,7 @@ const Thumbnail = ({ hasLinkDimensions = false; } - if (isText || isLink || isPdf || isSpoiler || (isNsfw && !isNsfwSubplebbit) || isNotFound) { + if (isText || isLink || isPdf || isSpoiler || (isNsfw && !isNsfwCommunity) || isNotFound) { displayWidth = '50px'; displayHeight = '50px'; hasLinkDimensions = true; @@ -147,7 +147,7 @@ const Thumbnail = ({ noMediaLinkIcon = 'spoiler'; } - if (isNsfw && blurNsfwThumbnails && !isNsfwSubplebbit) { + if (isNsfw && blurNsfwThumbnails && !isNsfwCommunity) { mediaComponent = ; noMediaLinkIcon = 'nsfw'; } @@ -179,7 +179,7 @@ const Thumbnail = ({ {mediaComponent} ) : ( - {mediaComponent} + {mediaComponent} )} diff --git a/src/components/reply-form/reply-form.tsx b/src/components/reply-form/reply-form.tsx index 6b3bbd6ba..be716c3aa 100644 --- a/src/components/reply-form/reply-form.tsx +++ b/src/components/reply-form/reply-form.tsx @@ -1,9 +1,10 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useSubplebbit } from '@bitsocialnet/bitsocial-react-hooks'; +import { useCommunity } from '@bitsocial/bitsocial-react-hooks'; import { isValidURL } from '../../lib/utils/url-utils'; -import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; +import useIsCommunityOffline from '../../hooks/use-is-community-offline'; import usePublishReply from '../../hooks/use-publish-reply'; +import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; import Markdown from '../markdown'; import styles from './reply-form.module.css'; @@ -11,7 +12,7 @@ type ReplyFormProps = { cid: string; isReplyingToReply?: boolean; hideReplyForm?: () => void; - subplebbitAddress: string; + communityAddress: string; postCid: string | undefined; }; @@ -109,21 +110,21 @@ export const FormattingHelpTable = () => { ); }; -const ReplyForm = ({ cid, isReplyingToReply, hideReplyForm, subplebbitAddress, postCid }: ReplyFormProps) => { +const ReplyForm = ({ cid, isReplyingToReply, hideReplyForm, communityAddress, postCid }: ReplyFormProps) => { const { t } = useTranslation(); const [showOptions, setShowOptions] = useState(false); const [showFormattingHelp, setShowFormattingHelp] = useState(false); const [isTextareaFocused, setIsTextareaFocused] = useState(false); const [showPreview, setShowPreview] = useState(false); - const { setPublishReplyOptions, resetPublishReplyOptions, replyIndex, publishReply, publishReplyOptions } = usePublishReply({ cid, subplebbitAddress, postCid }); + const { setPublishReplyOptions, resetPublishReplyOptions, replyIndex, publishReply, publishReplyOptions } = usePublishReply({ cid, communityAddress, postCid }); const mdContainerClass = isReplyingToReply ? `${styles.mdContainer} ${styles.mdContainerReplying}` : styles.mdContainer; const urlClass = showOptions ? styles.urlVisible : styles.urlHidden; const spoilerClass = showOptions ? styles.spoilerVisible : styles.spoilerHidden; const nsfwClass = showOptions ? styles.spoilerVisible : styles.spoilerHidden; - const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true }); - const { isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit); + const community = useCommunity(communityAddress ? { community: getCommunityIdentifier(communityAddress), onlyIfCached: true } : undefined); + const { isOffline, offlineTitle } = useIsCommunityOffline(community); // focus on the textarea when replying to a reply const textRef = useRef(null); diff --git a/src/components/reply/reply.tsx b/src/components/reply/reply.tsx index e386f8098..defa0c85d 100644 --- a/src/components/reply/reply.tsx +++ b/src/components/reply/reply.tsx @@ -1,21 +1,14 @@ import { Fragment, useEffect, useMemo, useState, useRef } from 'react'; import { Link, useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import Plebbit from '@plebbit/plebbit-js'; -import { - Comment, - useAccountComment, - useAuthorAddress, - useAuthorAvatar, - useBlock, - useComment, - useEditedComment, - useSubplebbit, -} from '@bitsocialnet/bitsocial-react-hooks'; +import { Comment, useAccountComment, useAuthorAddress, useAuthorAvatar, useBlock, useComment, useEditedComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import { isInboxView, isPostContextView, isPostPageView } from '../../lib/utils/view-utils'; +import getShortAddress from '../../lib/utils/address-utils'; +import { getCommentCommunityAddress } from '../../lib/utils/comment-utils'; +import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; import { getHostname } from '../../lib/utils/url-utils'; import { formatScore, getReplyScore } from '../../lib/utils/post-utils'; -import { flattenCommentsPages } from '@bitsocialnet/bitsocial-react-hooks/dist/lib/utils'; +import { flattenCommentsPages } from '@bitsocial/bitsocial-react-hooks/dist/lib/utils'; import { CommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; import { formatLocalizedUTCTimestamp, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { useCommentMediaInfo } from '../../hooks/use-comment-media-info'; @@ -48,7 +41,7 @@ interface ReplyAuthorProps { removed: boolean; shortAuthorAddress: string | undefined; submitterAddress: string; - subplebbitAddress: string; + communityAddress: string; postCid: string; pinned: boolean; } @@ -65,7 +58,7 @@ const ReplyAuthor = ({ removed, shortAuthorAddress, submitterAddress, - subplebbitAddress, + communityAddress, postCid, }: ReplyAuthorProps) => { const { t } = useTranslation(); @@ -109,7 +102,7 @@ const ReplyAuthor = ({ {' '} [ {isAuthorSubmitter && ( - + S )} @@ -126,7 +119,7 @@ const ReplyAuthor = ({ {' '} [ - + S ] @@ -214,19 +207,20 @@ type ParentLinkProps = { parentCid?: string; postCid?: string; shortAddress?: string; - subplebbitAddress?: string; + communityAddress?: string; timestamp?: number; }; const ParentLink = ({ postCid }: ParentLinkProps) => { const parentComment = useComment({ commentCid: postCid }); - const { author, cid, content, title, subplebbitAddress } = parentComment || {}; + const { author, cid, content, title } = parentComment || {}; + const communityAddress = getCommentCommunityAddress(parentComment); const { t } = useTranslation(); const postTitle = (title?.length > 300 ? title?.slice(0, 300) + '...' : title) || (content?.length > 300 ? content?.slice(0, 300) + '...' : content); return (
      - + {postTitle}{' '} {t('post_by')}{' '} @@ -234,8 +228,8 @@ const ParentLink = ({ postCid }: ParentLinkProps) => { u/{author?.shortAddress}{' '} {t('via')}{' '} - - s/{subplebbitAddress} + + s/{communityAddress}
      ); @@ -246,7 +240,8 @@ const InboxParentLink = ({ commentCid }: ParentLinkProps) => { const inboxComment = useComment({ commentCid, onlyIfCached: true }); const { postCid, parentCid } = inboxComment || {}; const parent = useComment({ commentCid: inboxComment?.postCid, onlyIfCached: true }); - const { cid, content, title, subplebbitAddress } = parent || {}; + const { cid, content, title } = parent || {}; + const communityAddress = getCommentCommunityAddress(parent); const postTitle = (title?.length > 300 ? title?.slice(0, 300) + '...' : title) || (content?.length > 300 ? content?.slice(0, 300) + '...' : content); const isInboxCommentReply = postCid !== parentCid; @@ -255,7 +250,7 @@ const InboxParentLink = ({ commentCid }: ParentLinkProps) => { return (
      {isInboxCommentReply ? t('comment_reply') : isInboxPostReply ? t('post_reply') : ''} - + {postTitle}
      @@ -265,11 +260,12 @@ const InboxParentLink = ({ commentCid }: ParentLinkProps) => { const InboxParentComment = ({ parentCid }: { parentCid: string | undefined }) => { const { t } = useTranslation(); const parentComment = useComment({ commentCid: parentCid }); - const { content, subplebbitAddress } = parentComment || {}; + const { content } = parentComment || {}; + const communityAddress = getCommentCommunityAddress(parentComment); return ( <> - + {t('view_parent_comment')} @@ -289,9 +285,9 @@ const InboxShowParentButton = ({ parentCid }: { parentCid: string | undefined }) ); }; -const InboxParentInfo = ({ address, cid, markedAsRead, parentCid, postCid, shortAddress, subplebbitAddress, timestamp }: ParentLinkProps) => { +const InboxParentInfo = ({ address, cid, markedAsRead, parentCid, postCid, shortAddress, communityAddress, timestamp }: ParentLinkProps) => { const { t } = useTranslation(); - const shortSubplebbitAddress = subplebbitAddress ? (subplebbitAddress.includes('.') ? subplebbitAddress : Plebbit.getShortAddress({ address: subplebbitAddress })) : ''; + const shortCommunityAddress = communityAddress ? (communityAddress.includes('.') ? communityAddress : getShortAddress(communityAddress)) : ''; return ( <> @@ -301,8 +297,8 @@ const InboxParentInfo = ({ address, cid, markedAsRead, parentCid, postCid, short u/{shortAddress}{' '} {t('via')}{' '} - - s/{shortSubplebbitAddress}{' '} + + s/{shortCommunityAddress}{' '} {t('sent')} {timestamp && getFormattedTimeAgo(timestamp)}
      @@ -347,11 +343,11 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl spoiler, nsfw, state, - subplebbitAddress, timestamp, upvoteCount, } = reply || {}; - const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true }); + const communityAddress = getCommentCommunityAddress(reply); + const community = useCommunity(communityAddress ? { community: getCommunityIdentifier(communityAddress), onlyIfCached: true } : undefined); const pendingReply = useAccountComment({ commentIndex: reply?.index }); const parentOfPendingReply = useComment({ commentCid: pendingReply?.parentCid, onlyIfCached: true }); @@ -362,7 +358,7 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl const isInPostContextView = isPostContextView(location.pathname, params, location.search); const isInPostPageView = isPostPageView(location.pathname, params); - const authorRole = subplebbit?.roles?.[author?.address]?.role; + const authorRole = community?.roles?.[author?.address]?.role; const { shortAuthorAddress } = useAuthorAddress({ comment: reply }); const { imageUrl } = useAuthorAvatar({ author }); const replies = useReplies(reply); @@ -470,7 +466,7 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl removed={removed} shortAuthorAddress={shortAuthorAddress} submitterAddress={post?.author?.address} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress || ''} pinned={pinned} postCid={postCid} /> @@ -505,7 +501,7 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl parentCid={parentCid} postCid={postCid} shortAddress={author?.shortAddress} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress} timestamp={timestamp} /> )} @@ -573,12 +569,12 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl postCid={postCid} removed={removed} replyCount={replies.length} - subplebbitAddress={subplebbitAddress} + communityAddress={communityAddress || ''} showCommentEditForm={showCommentEditForm} showReplyForm={showReplyForm} spoiler={spoiler} /> - {isReplying && } + {isReplying && } {!isSingleReply && replies.map((reply, index) => { return ( @@ -592,7 +588,7 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl /> ) : (
      - {t('continue_thread')} + {t('continue_thread')}
      )} diff --git a/src/components/search-bar/search-bar.tsx b/src/components/search-bar/search-bar.tsx index 498f11673..02e85886c 100644 --- a/src/components/search-bar/search-bar.tsx +++ b/src/components/search-bar/search-bar.tsx @@ -2,18 +2,18 @@ import { useCallback, useEffect, useRef, useState, useMemo } from 'react'; import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useFloating, autoUpdate, offset, shift, FloatingPortal } from '@floating-ui/react'; -import { useAccount } from '@bitsocialnet/bitsocial-react-hooks'; -import Plebbit from '@plebbit/plebbit-js'; +import { useAccount } from '@bitsocial/bitsocial-react-hooks'; import { isHomeView, isHomeAboutView, isPostPageView, isPostPageAboutView, - isSubplebbitView, + isCommunityView, isAllView, isModView, - isSubplebbitAboutView, + isCommunityAboutView, } from '../../lib/utils/view-utils'; +import getShortAddress from '../../lib/utils/address-utils'; import useFeedFiltersStore from '../../stores/use-feed-filters-store'; import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; import styles from './search-bar.module.css'; @@ -33,14 +33,14 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { const isInHomeAboutView = isHomeAboutView(location.pathname); const isInPostPageAboutView = isPostPageAboutView(location.pathname, params); - const isInSubplebbitView = isSubplebbitView(location.pathname, params); - const isInSubplebbitAboutView = isSubplebbitAboutView(location.pathname, params); + const isInCommunityView = isCommunityView(location.pathname, params); + const isInCommunityAboutView = isCommunityAboutView(location.pathname, params); const isInHomeView = isHomeView(location.pathname); const isInPostPageView = isPostPageView(location.pathname, params); const isInAllView = isAllView(location.pathname); const isInModView = isModView(location.pathname); - const isInFeedView = (isInSubplebbitView || isInHomeView || isInAllView || isInModView) && !isInPostPageView; + const isInFeedView = (isInCommunityView || isInHomeView || isInAllView || isInModView) && !isInPostPageView; const currentQuery = searchParams.get('q') || ''; const [isInCommunitySearch, setIsInCommunitySearch] = useState(() => { @@ -141,7 +141,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { } const searchInput = searchInputRef.current?.value; if (searchInput) { - if (searchInput.toLowerCase() === params.subplebbitAddress?.toLowerCase()) { + if (searchInput.toLowerCase() === params.communityAddress?.toLowerCase()) { alert(t('already_in_community')); return; } @@ -183,7 +183,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { const handleCommunitySelect = useCallback( (address: string) => { - if (address.toLowerCase() === params.subplebbitAddress?.toLowerCase()) { + if (address.toLowerCase() === params.communityAddress?.toLowerCase()) { alert(t('already_in_community')); return; } @@ -194,7 +194,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { searchInputRef.current?.blur(); navigate(`/s/${address}`); }, - [navigate, setInputValue, setIsInputFocused, setActiveDropdownIndex, params.subplebbitAddress, t], + [navigate, setInputValue, setIsInputFocused, setActiveDropdownIndex, params.communityAddress, t], ); const handleKeyDown = useCallback( @@ -228,7 +228,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { ); return ( -
      +
      { onTouchEnd={() => handleCommunitySelect(address)} onMouseEnter={() => setActiveDropdownIndex(index)} > - {Plebbit.getShortAddress({ address })} + {getShortAddress(address)} ))}
    diff --git a/src/components/sidebar/sidebar.module.css b/src/components/sidebar/sidebar.module.css index 0000f9952..61ae9398d 100644 --- a/src/components/sidebar/sidebar.module.css +++ b/src/components/sidebar/sidebar.module.css @@ -305,7 +305,7 @@ a { overflow: hidden; } -.subplebbitFooterMargin { +.communityFooterMargin { margin-top: 15px; margin-bottom: 15px; } diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index 0b8d672ba..d530ff274 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -1,11 +1,11 @@ import { useState, useEffect } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Comment, useAccount, useBlock, Role, Subplebbit, useSubplebbitStats, useAccountComment, usePlebbitRpcSettings } from '@bitsocialnet/bitsocial-react-hooks'; -import Plebbit from '@plebbit/plebbit-js'; +import { Comment, useAccount, useBlock, Role, Community, useCommunityStats, useAccountComment, usePkcRpcSettings } from '@bitsocial/bitsocial-react-hooks'; import { getPostScore } from '../../lib/utils/post-utils'; import { getFormattedDate, getFormattedTimeDuration, getFormattedTimeAgo } from '../../lib/utils/time-utils'; -import { findSubplebbitCreator } from '../../lib/utils/user-utils'; +import { findCommunityCreator } from '../../lib/utils/user-utils'; +import getShortAddress from '../../lib/utils/address-utils'; import { isAllView, isDomainView, @@ -15,14 +15,15 @@ import { isPendingPostView, isPostPageAboutView, isPostPageView, - isSubplebbitAboutView, - isSubplebbitSettingsView, - isSubplebbitsView, - isSubplebbitView, + isCommunityAboutView, + isCommunitySettingsView, + isCommunitiesView, + isCommunityView, } from '../../lib/utils/view-utils'; import useCommunitySubtitles from '../../hooks/use-community-subtitles'; import useIsMobile from '../../hooks/use-is-mobile'; -import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; +import useIsCommunityOffline from '../../hooks/use-is-community-offline'; +import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; import { FAQ } from '../../views/about/about'; import LoadingEllipsis from '../loading-ellipsis'; import Markdown from '../markdown'; @@ -55,7 +56,7 @@ const ModeratorsList = ({ roles }: { roles: Record }) => {