refactor: rename subplebbit -> community across seedit

Apply the broader subplebbit -> community rebrand on top of the
package switch:

- Rename hook usages (useSubplebbit -> useCommunity, useSubplebbits
  -> useCommunities, useSubplebbitStats -> useCommunityStats,
  useSubplebbitsStates -> useCommunitiesStates,
  useAccountSubplebbits -> useAccountCommunities,
  usePublishSubplebbitEdit -> usePublishCommunityEdit,
  usePlebbitRpcSettings -> usePkcRpcSettings) and types (Subplebbit
  -> Community, PublishSubplebbitEditOptions ->
  PublishCommunityEditOptions).
- Rename seedit identifiers (variables, types, store names) from
  subplebbit/Subplebbit to community/Community.
- git mv view directories, store files, hook files, and CSS modules
  from subplebbit-* to community-*. Update all imports.
- Rename the React Router :subplebbitAddress route param to
  :communityAddress (path /s/ unchanged).
- Update English translation values for community-related strings.
- Drop src/lib/bitsocial-react-hooks-compat.ts (no longer needed
  now that @bitsocial/bitsocial-react-hooks exposes the community
  names natively).
- Add src/hooks/use-community-identifier.ts and
  src/lib/utils/address-utils.ts as small adapters for the new
  CommunityIdentifier shape and short-address utility.
- Fix src/hooks/use-default-subplebbits.ts to import Community
  instead of Subplebbit (PR #813 will rename the file in a
  follow-up; tiny merge conflict expected on that one file).

Build, lint, and type-check all pass.
This commit is contained in:
Tommaso Casaburi
2026-04-27 14:38:23 +07:00
parent 8d017a1ab7
commit 309c553c5a
57 changed files with 740 additions and 985 deletions

View File

@@ -293,8 +293,8 @@
"more_posts_last_month": "{{count}} posts last {{currentTimeFilterName}}: <1>show more posts from last month</1>",
"profile_info": "Your account u/{{shortAddress}} was created. <1>Set display name</1>, <2>export backup</2>, <3>learn more</3>.",
"show_all_instead": "Showing posts since {{timeFilterName}}, <1>show all instead</1>",
"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</1> account backup",
"export_account_backup": "<1>export</1> account backup",
"save_reset_changes": "<1>save</1> or <2>reset</2> changes",

View File

@@ -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 = () => {
<Route path='/about' element={<AboutView />} />
<Route path='/submit' element={<SubmitPage />} />
<Route path='/s/:subplebbitAddress/c/:commentCid' element={<PostPage />} />
<Route path='/s/:subplebbitAddress/c/:commentCid/about' element={<AboutView />} />
<Route path='/s/:communityAddress/c/:commentCid' element={<PostPage />} />
<Route path='/s/:communityAddress/c/:commentCid/about' element={<AboutView />} />
<Route path='/s/:subplebbitAddress/submit' element={<SubmitPage />} />
<Route path='/s/:subplebbitAddress/about' element={<AboutView />} />
<Route path='/s/:communityAddress/submit' element={<SubmitPage />} />
<Route path='/s/:communityAddress/about' element={<AboutView />} />
<Route path='/settings' element={<Settings />} />
<Route path='/s/:subplebbitAddress/settings' element={<SubplebbitSettings />} />
<Route path='/s/:subplebbitAddress/settings/editor' element={<SubplebbitDataEditor />} />
<Route path='/s/:communityAddress/settings' element={<CommunitySettings />} />
<Route path='/s/:communityAddress/settings/editor' element={<CommunityDataEditor />} />
<Route path='/settings/plebbit-options' element={<Settings />} />
<Route path='/settings/content-options' element={<Settings />} />
<Route path='/settings/account-data' element={<AccountDataEditor />} />
@@ -112,15 +112,15 @@ const App = () => {
<Route path='/inbox/commentreplies' element={<Inbox />} />
<Route path='/inbox/postreplies' element={<Inbox />} />
<Route path='/communities' element={<Subplebbits />} />
<Route path='/communities/subscriber' element={<Subplebbits />} />
<Route path='/communities/moderator' element={<Subplebbits />} />
<Route path='/communities/admin' element={<Subplebbits />} />
<Route path='/communities/owner' element={<Subplebbits />} />
<Route path='/communities/vote' element={<Subplebbits />} />
<Route path='/communities/vote/passing' element={<Subplebbits />} />
<Route path='/communities/vote/rejecting' element={<Subplebbits />} />
<Route path='/communities/create' element={<SubplebbitSettings />} />
<Route path='/communities' element={<Communities />} />
<Route path='/communities/subscriber' element={<Communities />} />
<Route path='/communities/moderator' element={<Communities />} />
<Route path='/communities/admin' element={<Communities />} />
<Route path='/communities/owner' element={<Communities />} />
<Route path='/communities/vote' element={<Communities />} />
<Route path='/communities/vote/passing' element={<Communities />} />
<Route path='/communities/vote/rejecting' element={<Communities />} />
<Route path='/communities/create' element={<CommunitySettings />} />
</Route>
<Route element={feedLayout}>
<Route path='/:sortType?/:timeFilterName?' element={<Home />} />
@@ -129,7 +129,7 @@ const App = () => {
<Route path='/s/mod/:sortType?/:timeFilterName?' element={<Mod />} />
<Route path='/s/:subplebbitAddress/:sortType?/:timeFilterName?' element={<Subplebbit />} />
<Route path='/s/:communityAddress/:sortType?/:timeFilterName?' element={<CommunityView />} />
<Route path='/domain/:domain/:sortType?/:timeFilterName?' element={<Domain />} />

View File

@@ -3,26 +3,28 @@ import { Link, useLocation, useParams } from 'react-router-dom';
import {
useAccount,
useAccountComments,
useAccountSubplebbits,
AccountSubplebbit,
useAccountCommunities,
AccountCommunity,
Community,
useAuthor,
useAuthorAvatar,
useAuthorComments,
useBlock,
useComment,
useSubplebbits,
useCommunities,
} from '@bitsocial/bitsocial-react-hooks';
import Plebbit from '@plebbit/plebbit-js';
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 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<string, AccountSubplebbit>;
accountSubplebbits: Record<string, AccountCommunity & Partial<Community>>;
authorSubplebbits: string[];
isAuthor?: boolean;
}
@@ -39,7 +41,7 @@ const AuthorModeratingList = ({ accountSubplebbits, authorSubplebbits, isAuthor
<ul className={`${styles.modListContent} ${styles.modsList}`}>
{subplebbitAddresses.map((address, index) => (
<li key={index}>
<Link to={`/s/${address}`}>s/{Plebbit.getShortAddress({ address })}</Link>
<Link to={`/s/${address}`}>s/{getShortAddress(address)}</Link>
</li>
))}
</ul>
@@ -65,7 +67,7 @@ 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: accountSubplebbits } = useAccountCommunities();
const profileOldestAccountTimestamp = getOldestAccountHistoryTimestamp(oldestAccountComment as { timestamp?: number }[]);
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
@@ -73,10 +75,10 @@ const AuthorSidebar = () => {
const subscriptionsAndDefaults = [...accountSubscriptions, ...defaultSubplebbitAddresses];
const subplebbits =
useSubplebbits({
subplebbitAddresses: subscriptionsAndDefaults || [],
useCommunities({
communities: getCommunityIdentifiers(subscriptionsAndDefaults || []),
onlyIfCached: true,
}).subplebbits?.filter(Boolean) || [];
}).communities?.filter(Boolean) || [];
const authorAccount = useAuthor({ authorAddress, commentCid });
const { authorComments } = useAuthorComments({ authorAddress, commentCid });

View File

@@ -1,7 +1,6 @@
import { Link, useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAccount, useAccountComment, useSubplebbit } from '@bitsocial/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,23 +24,24 @@ 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';
@@ -56,10 +56,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 (
<li className={`${styles.about} ${isInHomeAboutView || isInSubplebbitAboutView || isInPostPageAboutView ? styles.selected : styles.choice}`}>
<li className={`${styles.about} ${isInHomeAboutView || isInCommunityAboutView || isInPostPageAboutView ? styles.selected : styles.choice}`}>
<Link to={aboutLink}>{t('about')}</Link>
</li>
);
@@ -76,7 +76,7 @@ const CommentsButton = () => {
return (
<li className={(isInPostPageView || isInPendingPostView) && !isInHomeAboutView && !isInPostPageAboutView ? styles.selected : styles.choice}>
<Link to={`/s/${params.subplebbitAddress}/c/${params.commentCid}`} onClick={(e) => isInPendingPostView && e.preventDefault()}>
<Link to={`/s/${params.communityAddress}/c/${params.commentCid}`} onClick={(e) => isInPendingPostView && e.preventDefault()}>
{t('comments')}
</Link>
</li>
@@ -89,18 +89,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
@@ -196,27 +196,27 @@ const InboxHeaderTabs = () => {
const SubplebbitsHeaderTabs = () => {
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 (
<>
<li className={`${isInSubplebbitsVoteView ? styles.selected : styles.choice}`}>
<li className={`${isInCommunitiesVoteView ? styles.selected : styles.choice}`}>
<Link to={'/communities/vote'}>{t('vote')}</Link>
</li>
<li
className={
isInSubplebbitsSubscriberView || isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView || isInSubplebbitsView
isInCommunitiesSubscriberView || isInCommunitiesModeratorView || isInCommunitiesAdminView || isInCommunitiesOwnerView || isInCommunitiesView
? styles.selected
: styles.choice
}
@@ -263,11 +263,11 @@ const HeaderTabs = () => {
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 +278,7 @@ const HeaderTabs = () => {
isInHomeView ||
isInHomeAboutView ||
isInPostPageAboutView ||
(isInSubplebbitView && !isInSubplebbitSubmitView && !isInSubplebbitSettingsView) ||
(isInCommunityView && !isInCommunitySubmitView && !isInCommunitySettingsView) ||
isInAllView ||
isInModView ||
isInDomainView
@@ -288,7 +288,7 @@ const HeaderTabs = () => {
return <AuthorHeaderTabs />;
} else if (isInInboxView) {
return <InboxHeaderTabs />;
} else if (isInSubplebbitsView && !isInCreateSubplebbitView) {
} else if (isInCommunitiesView && !isInCreateCommunityView) {
return <SubplebbitsHeaderTabs />;
} else if (isInSettingsView || isInSettingsPlebbitOptionsView || isInSettingsContentOptionsView) {
return <SettingsHeaderTabs />;
@@ -313,14 +313,14 @@ 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 subplebbitAddress = params.communityAddress;
const { hideAdultCommunities, hideGoreCommunities, hideAntiCommunities, hideVulgarCommunities } = useContentOptionsStore();
const hasUnhiddenAnyNsfwCommunity = !hideAdultCommunities || !hideGoreCommunities || !hideAntiCommunities || !hideVulgarCommunities;
@@ -328,27 +328,23 @@ const HeaderTitle = ({ title, pendingPostSubplebbitAddress }: { title: string; p
const subplebbitTitle = (
<Link to={`/s/${isInPendingPostView ? pendingPostSubplebbitAddress : subplebbitAddress}`}>
{title ||
(subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress })) ||
(pendingPostSubplebbitAddress && Plebbit.getShortAddress({ address: pendingPostSubplebbitAddress }))}
{title || (subplebbitAddress && getShortAddress(subplebbitAddress)) || (pendingPostSubplebbitAddress && getShortAddress(pendingPostSubplebbitAddress))}
</Link>
);
const domainTitle = <Link to={`/domain/${params.domain}`}>{params.domain}</Link>;
const submitTitle = <span className={styles.submitTitle}>{t('submit')}</span>;
const profileTitle = <Link to='/profile'>{account?.author?.shortAddress}</Link>;
const authorTitle = (
<Link to={`/u/${params.authorAddress}/c/${params.commentCid}`}>{params.authorAddress && Plebbit.getShortAddress({ address: params.authorAddress })}</Link>
);
const authorTitle = <Link to={`/u/${params.authorAddress}/c/${params.commentCid}`}>{params.authorAddress && getShortAddress(params.authorAddress)}</Link>;
if (isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity) {
return <span>{t('over_18')}</span>;
} else if (isInSubplebbitSubmitView) {
} else if (isInCommunitySubmitView) {
return (
<>
{subplebbitTitle}: {submitTitle}
</>
);
} else if (isInSubplebbitSettingsView) {
} else if (isInCommunitySettingsView) {
return (
<>
{subplebbitTitle}: <span className={styles.lowercase}>{t('community_settings')}</span>
@@ -360,15 +356,15 @@ const HeaderTitle = ({ title, pendingPostSubplebbitAddress }: { title: string; p
return t('preferences');
} else if (isInProfileView && !isInPendingPostView) {
return profileTitle;
} else if (isInPostPageView || isInPendingPostView || (isInSubplebbitView && !isInSubplebbitSettingsView)) {
} else if (isInPostPageView || isInPendingPostView || (isInCommunityView && !isInCommunitySettingsView)) {
return subplebbitTitle;
} else if (isInAuthorView) {
return authorTitle;
} else if (isInInboxView) {
return t('messages');
} else if (isInCreateSubplebbitView) {
} else if (isInCreateCommunityView) {
return <span className={styles.lowercase}>{t('create_community')}</span>;
} else if (isInSubplebbitsView) {
} else if (isInCommunitiesView) {
return t('communities');
} else if (isInNotFoundView) {
return <span className={styles.lowercase}>{t('page_not_found')}</span>;
@@ -387,7 +383,7 @@ const Header = () => {
const [theme] = useTheme();
const location = useLocation();
const params = useParams();
const subplebbit = useSubplebbit({ subplebbitAddress: params?.subplebbitAddress, onlyIfCached: true });
const subplebbit = useCommunity(params?.communityAddress ? { community: { name: params?.communityAddress }, onlyIfCached: true } : undefined);
const { suggested, title } = subplebbit || {};
const commentIndex = params?.accountCommentIndex ? parseInt(params?.accountCommentIndex) : undefined;
@@ -407,24 +403,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 +427,7 @@ const Header = () => {
(isInDomainView && !isInHomeAboutView) ||
(isInAuthorView && !isInHomeAboutView);
const subplebbitAddress = params.subplebbitAddress;
const subplebbitAddress = params.communityAddress;
const contentOptionsStore = useContentOptionsStore();
const hasUnhiddenAnyNsfwCommunity =
@@ -442,7 +437,7 @@ const Header = () => {
!contentOptionsStore.hideVulgarCommunities;
const isBroadlyNsfwSubplebbit = useIsBroadlyNsfwSubplebbit(subplebbitAddress || '');
const logoIsAvatar = isInSubplebbitView && suggested?.avatarUrl && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity);
const logoIsAvatar = isInCommunityView && suggested?.avatarUrl && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity);
const logoSrc = logoIsAvatar ? suggested?.avatarUrl : 'assets/sprout/sprout.png';
const logoLink = '/';
@@ -459,15 +454,15 @@ const Header = () => {
<div className={styles.header}>
<div
className={`${styles.container} ${hasFewTabs && styles.reducedHeight} ${
isInSubmitView && isInSubplebbitSubmitView && !isInSubplebbitView && isMobile && styles.reduceSubmitPageHeight
isInSubmitView && isInCommunitySubmitView && !isInCommunityView && isMobile && styles.reduceSubmitPageHeight
} ${hasStickyHeader && styles.increasedHeight}`}
>
<div className={styles.logoContainer}>
<Link to={logoLink} className={styles.logoLink}>
{(logoIsAvatar || (!isInSubplebbitView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && (
{(logoIsAvatar || (!isInCommunityView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && (
<img className={`${logoIsAvatar ? styles.avatar : styles.logo}`} src={logoSrc} alt='' />
)}
{((!isInSubplebbitView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && (
{((!isInCommunityView && !isInProfileView && !isInAuthorView) || !logoIsAvatar) && (
<img src={`assets/sprout/seedit-text-${theme === 'dark' ? 'dark' : 'light'}.svg`} className={styles.logoText} alt='' />
)}
</Link>
@@ -489,10 +484,10 @@ const Header = () => {
</ul>
)}
</div>
{isMobile && !isInSubplebbitSubmitView && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity) && (
{isMobile && !isInCommunitySubmitView && !(isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity) && (
<ul className={`${styles.tabMenu} ${isInProfileView ? styles.horizontalScroll : ''}`}>
<HeaderTabs />
{(isInHomeView || isInHomeAboutView || isInSubplebbitView || isInHomeAboutView || isInPostPageView) && <AboutButton />}
{(isInHomeView || isInHomeAboutView || isInCommunityView || isInHomeAboutView || isInPostPageView) && <AboutButton />}
{!isInSubmitView && !isInSettingsView && (
<li>
<Link to={mobileSubmitButtonRoute} className={styles.submitButton}>

View File

@@ -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 '@bitsocial/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';
@@ -296,7 +296,7 @@ const CommentTools = ({
}: CommentToolsProps) => {
const account = useAccount();
const isAuthor = account?.author?.address === author?.address;
const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true });
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress }, onlyIfCached: true } : undefined);
const accountAuthorRole = subplebbit?.roles?.[account?.author?.address]?.role;
const commentAuthorRole = subplebbit?.roles?.[author?.address]?.role;
const isAccountMod = accountAuthorRole === 'admin' || accountAuthorRole === 'owner' || accountAuthorRole === 'moderator';

View File

@@ -2,9 +2,9 @@ import { useState } from 'react';
import { useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Author, useBlock } from '@bitsocial/bitsocial-react-hooks';
import Plebbit from '@plebbit/plebbit-js';
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 = {
@@ -38,7 +38,7 @@ const BlockSubplebbitButton = ({ subplebbitAddress }: HideMenuProps) => {
return (
<div className={styles.menuItem} onClick={blocked ? unblock : block}>
{blocked ? `${t('unblock')}` : `${t('block')}`} s/{subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress })}
{blocked ? `${t('unblock')}` : `${t('block')}`} s/{subplebbitAddress && getShortAddress(subplebbitAddress)}
</div>
);
};

View File

@@ -79,7 +79,7 @@ const Expando = ({
mediaComponent = <Embed url={commentMediaInfo.url} />;
}
const pageSubplebbitAddress = useParams().subplebbitAddress;
const pageSubplebbitAddress = useParams().communityAddress;
const isNsfwSubplebbit = useIsNsfwSubplebbit(pageSubplebbitAddress || '');
return (

View File

@@ -1,13 +1,13 @@
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 '@bitsocial/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';
@@ -133,7 +133,7 @@ const Post = ({ index, post = {} }: PostProps) => {
const postDate = formatLocalizedUTCTimestamp(timestamp, language);
const params = useParams();
const location = useLocation();
const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true });
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress }, onlyIfCached: true } : undefined);
const authorRole = subplebbit?.roles?.[post.author?.address]?.role;
@@ -143,7 +143,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 +176,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: subplebbitAddress });
// show gray dotted border around last clicked post
const isLastClicked = sessionStorage.getItem('lastClickedPost') === cid && !isInPostPageView;
@@ -194,7 +194,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;
}
@@ -260,9 +260,7 @@ const Post = ({ index, post = {} }: PostProps) => {
{hostname ? (
<Link to={`/domain/${hostname}`}>{hostname.length > 25 ? hostname.slice(0, 25) + '...' : hostname}</Link>
) : (
<Link to={`/s/${subplebbitAddress}`}>
self.{subplebbit?.shortAddress || (subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress }))}
</Link>
<Link to={`/s/${subplebbitAddress}`}>self.{subplebbit?.shortAddress || (subplebbitAddress && getShortAddress(subplebbitAddress))}</Link>
)}
)
</span>
@@ -293,7 +291,7 @@ const Post = ({ index, post = {} }: PostProps) => {
shortAuthorAddress={shortAuthorAddress}
authorAddressChanged={authorAddressChanged}
/>
{!isInSubplebbitView && (
{!isInCommunityView && (
<>
 {t('post_to')}
<span className={styles.subscribeHoverGroup}>
@@ -309,7 +307,7 @@ const Post = ({ index, post = {} }: PostProps) => {
</span>
)}
<Link className={`${styles.subplebbit} ${subscribed && hasClickedSubscribe ? styles.greenSubplebbitAddress : ''}`} to={`/s/${subplebbitAddress}`}>
s/{subplebbit?.shortAddress || (subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress }))}
s/{subplebbit?.shortAddress || (subplebbitAddress && getShortAddress(subplebbitAddress))}
</Link>
</span>
</>

View File

@@ -45,7 +45,7 @@ const Thumbnail = ({
const thumbnailClass = expanded ? styles.thumbnailHidden : styles.thumbnailVisible;
const { blurNsfwThumbnails } = useContentOptionsStore();
const pageSubplebbitAddress = useParams().subplebbitAddress;
const pageSubplebbitAddress = useParams().communityAddress;
const isNsfwSubplebbit = useIsNsfwSubplebbit(pageSubplebbitAddress || '');
if (linkWidth && linkHeight) {

View File

@@ -1,8 +1,8 @@
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSubplebbit } from '@bitsocial/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 Markdown from '../markdown';
import styles from './reply-form.module.css';
@@ -122,8 +122,8 @@ const ReplyForm = ({ cid, isReplyingToReply, hideReplyForm, subplebbitAddress, p
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 subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress }, onlyIfCached: true } : undefined);
const { isOffline, offlineTitle } = useIsCommunityOffline(subplebbit);
// focus on the textarea when replying to a reply
const textRef = useRef<HTMLTextAreaElement>(null);

View File

@@ -1,9 +1,9 @@
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 '@bitsocial/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 { getHostname } from '../../lib/utils/url-utils';
import { formatScore, getReplyScore } from '../../lib/utils/post-utils';
import { flattenCommentsPages } from '@bitsocial/bitsocial-react-hooks/dist/lib/utils';
@@ -282,7 +282,7 @@ const InboxShowParentButton = ({ parentCid }: { parentCid: string | undefined })
const InboxParentInfo = ({ address, cid, markedAsRead, parentCid, postCid, shortAddress, subplebbitAddress, timestamp }: ParentLinkProps) => {
const { t } = useTranslation();
const shortSubplebbitAddress = subplebbitAddress ? (subplebbitAddress.includes('.') ? subplebbitAddress : Plebbit.getShortAddress({ address: subplebbitAddress })) : '';
const shortSubplebbitAddress = subplebbitAddress ? (subplebbitAddress.includes('.') ? subplebbitAddress : getShortAddress(subplebbitAddress)) : '';
return (
<>
@@ -342,7 +342,7 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl
timestamp,
upvoteCount,
} = reply || {};
const subplebbit = useSubplebbit({ subplebbitAddress, onlyIfCached: true });
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress }, onlyIfCached: true } : undefined);
const pendingReply = useAccountComment({ commentIndex: reply?.index });
const parentOfPendingReply = useComment({ commentCid: pendingReply?.parentCid, onlyIfCached: true });

View File

@@ -3,17 +3,17 @@ import { useLocation, useNavigate, useParams, useSearchParams } from 'react-rout
import { useTranslation } from 'react-i18next';
import { useFloating, autoUpdate, offset, shift, FloatingPortal } from '@floating-ui/react';
import { useAccount } from '@bitsocial/bitsocial-react-hooks';
import Plebbit from '@plebbit/plebbit-js';
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 (
<div ref={wrapperRef} className={`${styles.searchBarWrapper} ${isInHomeAboutView || isInSubplebbitAboutView || isInPostPageAboutView ? styles.mobileInfobar : ''}`}>
<div ref={wrapperRef} className={`${styles.searchBarWrapper} ${isInHomeAboutView || isInCommunityAboutView || isInPostPageAboutView ? styles.mobileInfobar : ''}`}>
<form className={styles.searchBar} ref={searchBarRef} onSubmit={handleSearchSubmit}>
<input
type='text'
@@ -272,7 +272,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => {
onTouchEnd={() => handleCommunitySelect(address)}
onMouseEnter={() => setActiveDropdownIndex(index)}
>
{Plebbit.getShortAddress({ address })}
{getShortAddress(address)}
</li>
))}
</ul>

View File

@@ -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 '@bitsocial/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 getShortAddress from '../../lib/utils/address-utils';
import {
isAllView,
isDomainView,
@@ -15,14 +15,14 @@ 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 { FAQ } from '../../views/about/about';
import LoadingEllipsis from '../loading-ellipsis';
import Markdown from '../markdown';
@@ -55,7 +55,7 @@ const ModeratorsList = ({ roles }: { roles: Record<string, Role> }) => {
<ul className={`${styles.listContent} ${styles.modsList}`}>
{rolesList.map(({ address }, index) => (
<li key={index} onClick={() => window.alert('Direct profile links are not supported yet.')}>
u/{Plebbit.getShortAddress({ address })}
u/{getShortAddress(address)}
</li>
))}
{/* TODO: https://github.com/bitsocialhq/seedit/issues/274
@@ -97,13 +97,13 @@ const ModerationTools = ({ address }: { address?: string }) => {
const { t } = useTranslation();
const location = useLocation();
const params = useParams();
const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params);
const isInCommunitySettingsView = isCommunitySettingsView(location.pathname, params);
return (
<div className={styles.list}>
<div className={styles.listTitle}>{t('moderation_tools')}</div>
<ul className={`${styles.listContent} ${styles.modsList}`}>
<li className={`${styles.moderationTool} ${isInSubplebbitSettingsView ? styles.selectedTool : ''}`}>
<li className={`${styles.moderationTool} ${isInCommunitySettingsView ? styles.selectedTool : ''}`}>
<Link className={styles.communitySettingsTool} to={`/s/${address}/settings`}>
{t('community_settings')}
</Link>
@@ -117,7 +117,7 @@ interface SidebarProps {
comment?: Comment;
isSubCreatedButNotYetPublished?: boolean;
settings?: any;
subplebbit?: Subplebbit;
subplebbit?: Community;
reset?: () => void;
}
@@ -127,12 +127,12 @@ export const Footer = () => {
const isMobile = useIsMobile();
const isInHomeAboutView = isHomeAboutView(location.pathname);
const isInPostPageAboutView = isPostPageAboutView(location.pathname, params);
const isInSubplebbitView = isSubplebbitView(location.pathname, params);
const isInCommunityView = isCommunityView(location.pathname, params);
return (
<div
className={`${styles.footer} ${isMobile && (isInHomeAboutView || isInPostPageAboutView) ? styles.mobileFooter : ''} ${
isInSubplebbitView ? styles.subplebbitFooterMargin : ''
isInCommunityView ? styles.subplebbitFooterMargin : ''
}`}
>
<div className={styles.footerLinks}>
@@ -173,8 +173,8 @@ export const Footer = () => {
const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit, reset }: SidebarProps) => {
const { t } = useTranslation();
const { address, createdAt, description, roles, rules, title, updatedAt } = subplebbit || {};
const { allActiveUserCount, hourActiveUserCount } = useSubplebbitStats({ subplebbitAddress: address });
const { isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit || {});
const { allActiveUserCount, hourActiveUserCount } = useCommunityStats(address ? { community: { name: address } } : undefined);
const { isOffline, offlineTitle } = useIsCommunityOffline(subplebbit || {});
const onlineNotice = t('users_online', { count: hourActiveUserCount || 0 });
const offlineNotice = updatedAt ? t('posts_last_synced', { dateAgo: getFormattedTimeAgo(updatedAt) }) : offlineTitle;
const onlineStatus = !isOffline ? onlineNotice : offlineNotice;
@@ -191,14 +191,14 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit
const isInModView = isModView(location.pathname);
const isInPendingPostView = isPendingPostView(location.pathname, params);
const isInPostPageView = isPostPageView(location.pathname, params);
const isInSubplebbitsView = isSubplebbitsView(location.pathname);
const isInSubplebbitAboutView = isSubplebbitAboutView(location.pathname, params);
const isInSubplebbitView = isSubplebbitView(location.pathname, params);
const isInCommunitiesView = isCommunitiesView(location.pathname);
const isInCommunityAboutView = isCommunityAboutView(location.pathname, params);
const isInCommunityView = isCommunityView(location.pathname, params);
const pendingPost = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
const subplebbitCreator = findSubplebbitCreator(roles);
const creatorAddress = subplebbitCreator === 'anonymous' ? 'anonymous' : `${Plebbit.getShortAddress({ address: subplebbitCreator })}`;
const creatorAddress = subplebbitCreator === 'anonymous' ? 'anonymous' : `${getShortAddress(subplebbitCreator)}`;
const submitRoute =
isInHomeView || isInHomeAboutView || isInAllView || isInModView || isInDomainView
? '/submit'
@@ -255,7 +255,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit
}
}, [communitySubtitles]);
const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected';
const isConnectedToRpc = usePkcRpcSettings()?.state === 'connected';
const navigate = useNavigate();
const handleCreateCommunity = () => {
// creating a community only works if the user is running a full node
@@ -292,7 +292,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit
}}
>
{(isInPostPageView || isInPendingPostView) && <PostInfo comment={comment} />}
{(isInSubplebbitView || isInHomeView || isInAllView || isInModView || isInDomainView || isInPendingPostView) && (
{(isInCommunityView || isInHomeView || isInAllView || isInModView || isInDomainView || isInPendingPostView) && (
<Link to={submitRoute}>
<div className={styles.largeButton}>
{t('submit_post')}
@@ -304,7 +304,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit
!isInHomeAboutView &&
!isInAllView &&
!isInModView &&
!isInSubplebbitsView &&
!isInCommunitiesView &&
!isInHomeAboutView &&
!isInDomainView &&
!isInPostPageAboutView && (
@@ -366,7 +366,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit
</div>
)}
{(moderatorRole || isOwner) && <ModerationTools address={address} />}
{isInSubplebbitsView && (
{isInCommunitiesView && (
<a href='https://github.com/bitsocialhq/lists' target='_blank' rel='noopener noreferrer'>
<div className={styles.largeButton}>
<div className={styles.nub} />
@@ -386,7 +386,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit
{subtitle2 && <div className={styles.createCommunitySubtitle}>{subtitle2}</div>}
</div>
{roles && Object.keys(roles).length > 0 && <ModeratorsList roles={roles} />}
{(!(isMobile && isInHomeAboutView) || isInSubplebbitAboutView || isInPostPageAboutView) && <Footer />}
{(!(isMobile && isInHomeAboutView) || isInCommunityAboutView || isInPostPageAboutView) && <Footer />}
{address && !(moderatorRole || isOwner) && (
<div className={styles.readOnlySettingsLink}>
<Link to={`/s/${address}/settings`}>{t('community_settings')}</Link>

View File

@@ -10,7 +10,7 @@ interface subscribeButtonProps {
}
const SubscribeButton = ({ address, onUnsubscribe }: subscribeButtonProps) => {
const { subscribe, subscribed, unsubscribe } = useSubscribe({ subplebbitAddress: address });
const { subscribe, subscribed, unsubscribe } = useSubscribe({ communityAddress: address });
const { t } = useTranslation();
const location = useLocation();
const params = useParams();

View File

@@ -1,9 +1,9 @@
import { useEffect, useRef, useState, useMemo, memo } from 'react';
import { Link, useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAccount, useAccountSubplebbits } from '@bitsocial/bitsocial-react-hooks';
import Plebbit from '@plebbit/plebbit-js';
import { isAllView, isDomainView, isHomeView, isModView, isSubplebbitView } from '../../lib/utils/view-utils';
import { useAccount, useAccountCommunities } from '@bitsocial/bitsocial-react-hooks';
import { isAllView, isDomainView, isHomeView, isModView, isCommunityView } from '../../lib/utils/view-utils';
import getShortAddress from '../../lib/utils/address-utils';
import useContentOptionsStore from '../../stores/use-content-options-store';
import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import useTimeFilter, { setSessionTimeFilterPreference } from '../../hooks/use-time-filter';
@@ -42,7 +42,7 @@ const CommunitiesDropdown = () => {
<div className={`${styles.dropChoices} ${styles.subsDropChoices} ${subsDropdownClass}`} ref={subsdropdownItemsRef}>
{reversedSubscriptions?.map((subscription: string, index: number) => (
<Link key={index} to={`/s/${subscription}`} className={styles.dropdownItem}>
{Plebbit.getShortAddress({ address: subscription })}
{getShortAddress(subscription)}
</Link>
))}
<Link to='/communities/subscriber' className={`${styles.dropdownItem} ${styles.myCommunitiesItemButtonDotted}`}>
@@ -168,7 +168,7 @@ const SortTypesDropdown = () => {
const { t } = useTranslation();
const params = useParams();
const location = useLocation();
const isInSubplebbitView = isSubplebbitView(location.pathname, params);
const isInCommunityView = isCommunityView(location.pathname, params);
const isinAllView = isAllView(location.pathname);
const { timeFilterName } = useTimeFilter();
@@ -203,7 +203,7 @@ const SortTypesDropdown = () => {
<span className={styles.selectedTitle}>{t(getSelectedSortLabel())}</span>
<div className={`${styles.dropChoices} ${styles.sortsDropChoices} ${sortsDropdownClass}`} ref={sortsdropdownItemsRef}>
{sortTypes.map((sortType, index) => {
let dropdownLink = isInSubplebbitView ? `/s/${params.subplebbitAddress}/${sortType}` : isinAllView ? `/s/all/${sortType}` : sortType;
let dropdownLink = isInCommunityView ? `/s/${params.communityAddress}/${sortType}` : isinAllView ? `/s/all/${sortType}` : sortType;
if (timeFilterName) {
dropdownLink += `/${timeFilterName}`;
}
@@ -221,12 +221,12 @@ const SortTypesDropdown = () => {
const TimeFilterDropdown = () => {
const params = useParams();
const location = useLocation();
const isInSubplebbitView = isSubplebbitView(location.pathname, params);
const isInCommunityView = isCommunityView(location.pathname, params);
const isInDomainView = isDomainView(location.pathname);
const isinAllView = isAllView(location.pathname);
const isInModView = isModView(location.pathname);
const { timeFilterName, timeFilterNames, sessionKey } = useTimeFilter();
const selectedTimeFilter = timeFilterName || (isInSubplebbitView ? 'all' : timeFilterName);
const selectedTimeFilter = timeFilterName || (isInCommunityView ? 'all' : timeFilterName);
const [isTimeFilterDropdownOpen, setIsTimeFilterDropdownOpen] = useState(false);
const toggleTimeFilterDropdown = () => setIsTimeFilterDropdownOpen(!isTimeFilterDropdownOpen);
@@ -237,8 +237,8 @@ const TimeFilterDropdown = () => {
const selectedSortType = params.sortType || 'hot';
const getTimeFilterLink = (timeFilterName: string) => {
return isInSubplebbitView
? `/s/${params.subplebbitAddress}/${selectedSortType}/${timeFilterName}`
return isInCommunityView
? `/s/${params.communityAddress}/${selectedSortType}/${timeFilterName}`
: isinAllView
? `s/all/${selectedSortType}/${timeFilterName}`
: isInModView
@@ -292,7 +292,7 @@ const TopBar = memo(() => {
const { hideDefaultCommunities } = useContentOptionsStore();
const subplebbitAddresses = useDefaultSubplebbitAddresses();
const { accountSubplebbits } = useAccountSubplebbits();
const { accountCommunities: accountSubplebbits } = useAccountCommunities();
const accountSubplebbitAddresses = useMemo(() => Object.keys(accountSubplebbits), [accountSubplebbits]);
const account = useAccount();
@@ -331,12 +331,12 @@ const TopBar = memo(() => {
)}
{subscriptions?.length > 0 && <span className={styles.separator}> | </span>}
{reversedSubscriptions?.map((subscription: string, index: number) => {
const shortAddress = Plebbit.getShortAddress({ address: subscription });
const shortAddress = getShortAddress(subscription);
const displayAddress = shortAddress.includes('.eth') ? shortAddress.slice(0, -4) : shortAddress.includes('.sol') ? shortAddress.slice(0, -4) : shortAddress;
return (
<li key={index}>
{index !== 0 && <span className={styles.separator}>-</span>}
<Link to={`/s/${subscription}`} className={params.subplebbitAddress === subscription ? styles.selected : styles.choice}>
<Link to={`/s/${subscription}`} className={params.communityAddress === subscription ? styles.selected : styles.choice}>
{displayAddress}
</Link>
</li>
@@ -345,7 +345,7 @@ const TopBar = memo(() => {
{!hideDefaultCommunities && filteredSubplebbitAddresses?.length > 0 && <span className={styles.separator}> | </span>}
{!hideDefaultCommunities &&
filteredSubplebbitAddresses?.map((address, index) => {
const shortAddress = Plebbit.getShortAddress({ address });
const shortAddress = getShortAddress(address);
const displayAddress = shortAddress.includes('.eth')
? shortAddress.slice(0, -4)
: shortAddress.includes('.sol')
@@ -354,7 +354,7 @@ const TopBar = memo(() => {
return (
<li key={index}>
{index !== 0 && <span className={styles.separator}>-</span>}
<Link to={`/s/${address}`} className={params.subplebbitAddress === address ? styles.selected : styles.choice}>
<Link to={`/s/${address}`} className={params.communityAddress === address ? styles.selected : styles.choice}>
{displayAddress}
</Link>
</li>

View File

@@ -1,7 +1,7 @@
import { usePlebbitRpcSettings } from '@bitsocial/bitsocial-react-hooks';
import { usePkcRpcSettings } from '@bitsocial/bitsocial-react-hooks';
const useChallengeSettings = (challengeName: string) => {
const { challenges } = usePlebbitRpcSettings().plebbitRpcSettings || {};
const { challenges } = usePkcRpcSettings().pkcRpcSettings || {};
if (challenges) {
return challenges[challengeName] || {};
}

View File

@@ -1,7 +1,7 @@
import { usePlebbitRpcSettings } from '@bitsocial/bitsocial-react-hooks';
import { usePkcRpcSettings } from '@bitsocial/bitsocial-react-hooks';
const useChallengesOptions = () => {
const { challenges } = usePlebbitRpcSettings().plebbitRpcSettings || {};
const { challenges } = usePkcRpcSettings().pkcRpcSettings || {};
const options = Object.keys(challenges || {}).reduce(
(acc, challengeName) => {

View File

@@ -0,0 +1,25 @@
import { useMemo } from 'react';
import type { CommunityIdentifier } from '@bitsocial/bitsocial-react-hooks';
const isLikelyCommunityName = (value: string) => value.includes('.');
export const getCommunityIdentifier = (communityAddress: string | undefined): CommunityIdentifier | undefined => {
if (!communityAddress) {
return undefined;
}
return isLikelyCommunityName(communityAddress) ? { name: communityAddress } : { publicKey: communityAddress };
};
export const getCommunityIdentifiers = (communityAddresses: Array<string | undefined>): CommunityIdentifier[] =>
communityAddresses.flatMap((communityAddress) => {
const community = getCommunityIdentifier(communityAddress);
return community ? [community] : [];
});
export const useCommunityIdentifier = (communityAddress: string | undefined): CommunityIdentifier | undefined => {
return useMemo(() => getCommunityIdentifier(communityAddress), [communityAddress]);
};
export const useCommunityIdentifiers = (communityAddresses?: Array<string | undefined>): CommunityIdentifier[] => {
return useMemo(() => getCommunityIdentifiers(communityAddresses ?? []), [communityAddresses]);
};

View File

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { Subplebbit } from '@bitsocial/bitsocial-react-hooks';
import { Community } from '@bitsocial/bitsocial-react-hooks';
import useContentOptionsStore from '../stores/use-content-options-store';
export interface MultisubMetadata {
@@ -109,7 +109,7 @@ export const useDefaultSubplebbitAddresses = () => {
const { hideAdultCommunities, hideGoreCommunities, hideAntiCommunities, hideVulgarCommunities } = useContentOptionsStore();
const filteredSubplebbits = useMemo(() => {
return defaultSubplebbits.filter((subplebbit: Subplebbit) => {
return defaultSubplebbits.filter((subplebbit: Community) => {
const tags = subplebbit.tags || [];
if (hideAdultCommunities && tags.includes('adult')) return false;
if (hideGoreCommunities && tags.includes('gore')) return false;

View File

@@ -0,0 +1,51 @@
import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';
import { Community } from '@bitsocial/bitsocial-react-hooks';
import { getFormattedTimeAgo } from '../lib/utils/time-utils';
import useCommunityOfflineStore from '../stores/use-community-offline-store';
import useCommunitiesLoadingStartTimestamps from '../stores/use-communities-loading-start-timestamps-store';
const useIsCommunityOffline = (subplebbit: Community) => {
const { t } = useTranslation();
const { address, state, updatedAt, updatingState } = subplebbit || {};
const { communityOfflineState, setCommunityOfflineState, initializeCommunityOfflineState } = useCommunityOfflineStore();
const communitiesLoadingStartTimestamps = useCommunitiesLoadingStartTimestamps([address]);
useEffect(() => {
if (address && !communityOfflineState[address]) {
initializeCommunityOfflineState(address);
}
}, [address, communityOfflineState, initializeCommunityOfflineState]);
useEffect(() => {
if (address) {
setCommunityOfflineState(address, { state, updatedAt, updatingState });
}
}, [address, state, updatedAt, updatingState, setCommunityOfflineState]);
const communityOfflineStore = communityOfflineState[address] || { initialLoad: true };
const loadingStartTimestamp = communitiesLoadingStartTimestamps[0] || 0;
const isLoading = communityOfflineStore.initialLoad && (!updatedAt || Date.now() / 1000 - updatedAt >= 120 * 60) && Date.now() / 1000 - loadingStartTimestamp < 30;
const isOffline = !isLoading && ((updatedAt && updatedAt < Date.now() / 1000 - 120 * 60) || (!updatedAt && Date.now() / 1000 - loadingStartTimestamp >= 30));
const isOnline = updatedAt && Date.now() / 1000 - updatedAt < 120 * 60;
const offlineTitle = isLoading
? t('loading')
: updatedAt
? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
: t('subplebbit_offline_info');
// ensure isOffline is false until we have enough information
const hasEnoughInfo = communityOfflineStore.initialLoad === false || updatedAt !== undefined;
return {
isOffline: hasEnoughInfo && !isOnline && isOffline,
isOnlineStatusLoading: !isOnline && isLoading,
offlineTitle,
};
};
export default useIsCommunityOffline;

View File

@@ -1,51 +0,0 @@
import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';
import { Subplebbit } from '@bitsocial/bitsocial-react-hooks';
import { getFormattedTimeAgo } from '../lib/utils/time-utils';
import useSubplebbitOfflineStore from '../stores/use-subplebbit-offline-store';
import useSubplebbitsLoadingStartTimestamps from '../stores/use-subplebbits-loading-start-timestamps-store';
const useIsSubplebbitOffline = (subplebbit: Subplebbit) => {
const { t } = useTranslation();
const { address, state, updatedAt, updatingState } = subplebbit || {};
const { subplebbitOfflineState, setSubplebbitOfflineState, initializesubplebbitOfflineState } = useSubplebbitOfflineStore();
const subplebbitsLoadingStartTimestamps = useSubplebbitsLoadingStartTimestamps([address]);
useEffect(() => {
if (address && !subplebbitOfflineState[address]) {
initializesubplebbitOfflineState(address);
}
}, [address, subplebbitOfflineState, initializesubplebbitOfflineState]);
useEffect(() => {
if (address) {
setSubplebbitOfflineState(address, { state, updatedAt, updatingState });
}
}, [address, state, updatedAt, updatingState, setSubplebbitOfflineState]);
const subplebbitOfflineStore = subplebbitOfflineState[address] || { initialLoad: true };
const loadingStartTimestamp = subplebbitsLoadingStartTimestamps[0] || 0;
const isLoading = subplebbitOfflineStore.initialLoad && (!updatedAt || Date.now() / 1000 - updatedAt >= 120 * 60) && Date.now() / 1000 - loadingStartTimestamp < 30;
const isOffline = !isLoading && ((updatedAt && updatedAt < Date.now() / 1000 - 120 * 60) || (!updatedAt && Date.now() / 1000 - loadingStartTimestamp >= 30));
const isOnline = updatedAt && Date.now() / 1000 - updatedAt < 120 * 60;
const offlineTitle = isLoading
? t('loading')
: updatedAt
? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
: t('subplebbit_offline_info');
// ensure isOffline is false until we have enough information
const hasEnoughInfo = subplebbitOfflineStore.initialLoad === false || updatedAt !== undefined;
return {
isOffline: hasEnoughInfo && !isOnline && isOffline,
isOnlineStatusLoading: !isOnline && isLoading,
offlineTitle,
};
};
export default useIsSubplebbitOffline;

View File

@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { useClientsStates, useSubplebbit, useSubplebbitsStates } from '@bitsocial/bitsocial-react-hooks';
import { useClientsStates, useCommunity, useCommunitiesStates } from '@bitsocial/bitsocial-react-hooks';
import { debounce } from 'lodash';
import { getCommunityIdentifiers } from './use-community-identifier';
interface CommentOrSubplebbit {
state?: string;
@@ -81,11 +82,11 @@ const useStateString = (commentOrSubplebbit: CommentOrSubplebbit): string | unde
export const useFeedStateString = (subplebbitAddresses?: string[]): string | undefined => {
// single subplebbit feed state string
const subplebbitAddress = subplebbitAddresses?.length === 1 ? subplebbitAddresses[0] : undefined;
const subplebbit = useSubplebbit({ subplebbitAddress });
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress } } : undefined);
const singleSubplebbitFeedStateString = useStateString(subplebbit);
// multiple subplebbit feed state string
const { states } = useSubplebbitsStates({ subplebbitAddresses });
const { states } = useCommunitiesStates({ communities: getCommunityIdentifiers(subplebbitAddresses || []) });
const multipleSubplebbitsFeedStateString = useMemo(() => {
if (subplebbitAddress) {
@@ -96,25 +97,25 @@ export const useFeedStateString = (subplebbitAddresses?: string[]): string | und
let stateString = '';
if (states['resolving-address']) {
const { subplebbitAddresses, clientUrls } = states['resolving-address'];
if (subplebbitAddresses.length && clientUrls.length) {
stateString += `resolving ${subplebbitAddresses.length} ${subplebbitAddresses.length === 1 ? 'address' : 'addresses'} from ${clientUrls
const { communityAddresses, clientUrls } = states['resolving-address'];
if (communityAddresses.length && clientUrls.length) {
stateString += `resolving ${communityAddresses.length} ${communityAddresses.length === 1 ? 'address' : 'addresses'} from ${clientUrls
.map(getClientHost)
.join(', ')}`;
}
}
// find all page client and sub addresses
// find all page client and community addresses
const pagesStatesClientHosts = new Set();
const pagesStatesSubplebbitAddresses = new Set();
const pagesStatesCommunityAddresses = new Set();
for (const state in states) {
if (state.match('page')) {
states[state].clientUrls.forEach((clientUrl) => pagesStatesClientHosts.add(getClientHost(clientUrl)));
states[state].subplebbitAddresses.forEach((subplebbitAddress) => pagesStatesSubplebbitAddresses.add(subplebbitAddress));
states[state].communityAddresses.forEach((communityAddress) => pagesStatesCommunityAddresses.add(communityAddress));
}
}
if (states['fetching-ipns'] || states['fetching-ipfs'] || pagesStatesSubplebbitAddresses.size) {
if (states['fetching-ipns'] || states['fetching-ipfs'] || pagesStatesCommunityAddresses.size) {
// separate 2 different states using ', '
if (stateString) {
stateString += ', ';
@@ -128,21 +129,19 @@ export const useFeedStateString = (subplebbitAddresses?: string[]): string | und
if (clientHosts.size) {
stateString += 'downloading ';
if (states['fetching-ipns']) {
stateString += `${states['fetching-ipns'].subplebbitAddresses.length} ${
states['fetching-ipns'].subplebbitAddresses.length === 1 ? 'community' : 'communities'
}`;
stateString += `${states['fetching-ipns'].communityAddresses.length} ${states['fetching-ipns'].communityAddresses.length === 1 ? 'community' : 'communities'}`;
}
if (states['fetching-ipfs']) {
if (states['fetching-ipns']) {
stateString += ', ';
}
stateString += `${states['fetching-ipfs'].subplebbitAddresses.length} ${states['fetching-ipfs'].subplebbitAddresses.length === 1 ? 'post' : 'posts'}`;
stateString += `${states['fetching-ipfs'].communityAddresses.length} ${states['fetching-ipfs'].communityAddresses.length === 1 ? 'post' : 'posts'}`;
}
if (pagesStatesSubplebbitAddresses.size) {
if (pagesStatesCommunityAddresses.size) {
if (states['fetching-ipns'] || states['fetching-ipfs']) {
stateString += ', ';
}
stateString += `${pagesStatesSubplebbitAddresses.size} ${pagesStatesSubplebbitAddresses.size === 1 ? 'page' : 'pages'}`;
stateString += `${pagesStatesCommunityAddresses.size} ${pagesStatesCommunityAddresses.size === 1 ? 'page' : 'pages'}`;
}
stateString += ` from ${[...clientHosts].join(', ')}`;
}

View File

@@ -1,7 +1,7 @@
import assert from 'assert';
import { useEffect } from 'react';
import { useLocation, useParams, Params } from 'react-router-dom';
import { isSubplebbitView, isAllView, isModView, isHomeView, isDomainView } from '../lib/utils/view-utils';
import { isCommunityView, isAllView, isModView, isHomeView, isDomainView } from '../lib/utils/view-utils';
// the timestamp the last time the user visited
const lastVisitTimestamp = localStorage.getItem('seeditLastVisitTimestamp');
@@ -85,7 +85,7 @@ const getSessionKeyForView = (pathname: string, params: Readonly<Params<string>>
if (isAllView(pathname)) return 'sessionTimeFilter-all';
if (isModView(pathname)) return 'sessionTimeFilter-mod';
if (isDomainView(pathname)) return `sessionTimeFilter-domain-${params.domain}`;
if (isSubplebbitView(pathname, params)) return `sessionTimeFilter-subplebbit-${params.subplebbitAddress}`;
if (isCommunityView(pathname, params)) return `sessionTimeFilter-community-${params.communityAddress}`;
return null;
};
@@ -130,7 +130,7 @@ export const isValidTimeFilterName = (name: string | undefined | null): boolean
const useTimeFilter = () => {
const params = useParams();
const location = useLocation();
const isInSubplebbitView = isSubplebbitView(location.pathname, params);
const isInCommunityView = isCommunityView(location.pathname, params);
const isInDomainView = Boolean(params.domain);
const sessionKey = getSessionKeyForView(location.pathname, params);
@@ -152,7 +152,7 @@ const useTimeFilter = () => {
// let the redirect logic in the component handle it.
// Just use it for calculating initial timeFilterSeconds if needed below.
} else {
if (isInSubplebbitView) {
if (isInCommunityView) {
timeFilterName = 'all';
} else if (isInDomainView) {
timeFilterName = '1y';

View File

@@ -1,283 +0,0 @@
import { useMemo } from 'react';
import {
deleteCommunity as deleteCommunityBase,
setAccount as setAccountBase,
useAccountComment as useAccountCommentBase,
useAccountCommunities as useAccountCommunitiesBase,
useCommunities as useCommunitiesBase,
useCommunitiesStates as useCommunitiesStatesBase,
useCommunity as useCommunityBase,
useCommunityStats as useCommunityStatsBase,
useCreateCommunity as useCreateCommunityBase,
useFeed as useFeedBase,
usePublishCommunityEdit as usePublishCommunityEditBase,
useSubscribe as useSubscribeBase,
} from '@bitsocial/bitsocial-react-hooks/dist/index.js';
import type {
Account,
AccountCommunity,
Community,
PublishCommunityEditOptions,
UseAccountCommentOptions as BaseUseAccountCommentOptions,
UseAccountCommentResult as BaseUseAccountCommentResult,
UseAccountCommunitiesOptions,
UseAccountCommunitiesResult,
UseCommunitiesOptions,
UseCommunitiesResult,
UseCommunitiesStatesOptions,
UseCommunitiesStatesResult,
UseCommunityOptions,
UseCommunityResult,
UseCommunityStatsOptions,
UseCommunityStatsResult,
UseCreateCommunityOptions,
UseCreateCommunityResult,
UseFeedOptions as BaseUseFeedOptions,
UseFeedResult as BaseUseFeedResult,
UsePublishCommunityEditOptions,
UsePublishCommunityEditResult,
UseSubscribeOptions as BaseUseSubscribeOptions,
} from '@bitsocial/bitsocial-react-hooks/dist/index.js';
import accountsStore from '@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js';
export * from '@bitsocial/bitsocial-react-hooks/dist/index.js';
type AliasAddresses<T extends { address?: string; subplebbitAddress?: string } | undefined> = T;
const withSubplebbitAddress = <T extends { address?: string; subplebbitAddress?: string } | undefined>(value: T): AliasAddresses<T> => {
if (!value || value.subplebbitAddress || !value.address) {
return value;
}
return { ...value, subplebbitAddress: value.address } as AliasAddresses<T>;
};
export type Subplebbit = Community;
export type AccountSubplebbit = AccountCommunity & Partial<Community>;
export type PublishSubplebbitEditOptions = PublishCommunityEditOptions;
export interface UseSubplebbitOptions extends Omit<UseCommunityOptions, 'communityAddress'> {
subplebbitAddress?: string;
}
export interface UseSubplebbitsOptions extends Omit<UseCommunitiesOptions, 'communityAddresses'> {
subplebbitAddresses?: string[];
}
export interface UseSubplebbitStatsOptions extends Omit<UseCommunityStatsOptions, 'communityAddress'> {
subplebbitAddress?: string;
}
export interface UseAccountSubplebbitsResult extends Omit<UseAccountCommunitiesResult, 'accountCommunities'> {
accountSubplebbits: UseAccountCommunitiesResult['accountCommunities'];
}
export interface UseFeedOptions extends Omit<BaseUseFeedOptions, 'communityAddresses'> {
communityAddresses?: string[];
subplebbitAddresses?: string[];
}
export interface UseFeedResult extends BaseUseFeedResult {
subplebbitAddressesWithNewerPosts: string[];
}
export interface UseSubscribeOptions extends Omit<BaseUseSubscribeOptions, 'communityAddress'> {
communityAddress?: string;
subplebbitAddress?: string;
}
export interface UseSubplebbitsStatesOptions extends Omit<UseCommunitiesStatesOptions, 'communityAddresses'> {
communityAddresses?: string[];
subplebbitAddresses?: string[];
}
export interface UseSubplebbitsStatesResult extends Omit<UseCommunitiesStatesResult, 'states'> {
states: {
[state: string]: {
subplebbitAddresses: string[];
clientUrls: string[];
};
};
}
export interface UsePublishSubplebbitEditOptions extends Omit<UsePublishCommunityEditOptions, 'communityAddress'> {
communityAddress?: string;
subplebbitAddress?: string;
}
export interface UsePublishSubplebbitEditResult extends Omit<UsePublishCommunityEditResult, 'publishCommunityEdit'> {
publishSubplebbitEdit(): Promise<void>;
}
export type UseCreateSubplebbitOptions = UseCreateCommunityOptions;
export interface UseCreateSubplebbitResult extends Omit<UseCreateCommunityResult, 'createdCommunity' | 'createCommunity'> {
createdSubplebbit: UseCreateCommunityResult['createdCommunity'];
createSubplebbit(): Promise<void>;
}
export interface UseAccountCommentOptions extends BaseUseAccountCommentOptions {}
export interface UseAccountCommentResult extends BaseUseAccountCommentResult {
refresh(): Promise<void>;
}
const normalizeAccountForSet = (account: Account): Account => {
const storedAccount = account?.id ? accountsStore.getState().accounts[account.id] : undefined;
if (!storedAccount) {
return account;
}
return {
...storedAccount,
...account,
author: account.author ? { ...storedAccount.author, ...account.author } : storedAccount.author,
subscriptions: account.subscriptions ?? storedAccount.subscriptions,
blockedAddresses: account.blockedAddresses ?? storedAccount.blockedAddresses,
blockedCids: account.blockedCids ?? storedAccount.blockedCids,
communities: account.communities ?? storedAccount.communities,
plebbitOptions: account.plebbitOptions ?? storedAccount.plebbitOptions,
mediaIpfsGatewayUrl: account.mediaIpfsGatewayUrl ?? storedAccount.mediaIpfsGatewayUrl,
};
};
export const useSubplebbit = (options?: UseSubplebbitOptions): UseCommunityResult => {
const { subplebbitAddress, ...rest } = options || {};
const community = useCommunityBase({ communityAddress: subplebbitAddress, ...rest });
return useMemo(() => withSubplebbitAddress(community as any) as UseCommunityResult, [community]);
};
export const useSubplebbits = (options?: UseSubplebbitsOptions): UseCommunitiesResult & { subplebbits: UseCommunitiesResult['communities'] } => {
const { subplebbitAddresses, ...rest } = options || {};
const result = useCommunitiesBase({ communityAddresses: subplebbitAddresses, ...rest });
return useMemo(
() => ({
...result,
communities: result.communities.map((community) => withSubplebbitAddress(community)),
subplebbits: result.communities.map((community) => withSubplebbitAddress(community)),
}),
[result],
);
};
export const useSubplebbitStats = (options?: UseSubplebbitStatsOptions): UseCommunityStatsResult => {
const { subplebbitAddress, ...rest } = options || {};
return useCommunityStatsBase({ communityAddress: subplebbitAddress, ...rest });
};
export const useAccountSubplebbits = (options?: UseAccountCommunitiesOptions): UseAccountSubplebbitsResult => {
const result = useAccountCommunitiesBase(options);
return useMemo(
() => ({
...result,
accountSubplebbits: result.accountCommunities,
}),
[result],
);
};
export const useFeed = (options: UseFeedOptions): UseFeedResult => {
const { subplebbitAddresses, communityAddresses, ...rest } = options;
const result = useFeedBase({
communityAddresses: communityAddresses || subplebbitAddresses || [],
...rest,
});
return useMemo(
() => ({
...result,
subplebbitAddressesWithNewerPosts: result.communityAddressesWithNewerPosts,
}),
[result],
);
};
export const useSubscribe = (options?: UseSubscribeOptions) => {
const { subplebbitAddress, communityAddress, ...rest } = options || {};
return useSubscribeBase({ communityAddress: communityAddress || subplebbitAddress, ...rest });
};
export const useSubplebbitsStates = (options?: UseSubplebbitsStatesOptions): UseSubplebbitsStatesResult => {
const { subplebbitAddresses, communityAddresses, ...rest } = options || {};
const result = useCommunitiesStatesBase({
communityAddresses: communityAddresses || subplebbitAddresses,
...rest,
});
return useMemo(
() => ({
...result,
states: Object.fromEntries(
Object.entries(result.states).map(([state, value]) => [
state,
{
subplebbitAddresses: value.communityAddresses,
clientUrls: value.clientUrls,
},
]),
),
}),
[result],
);
};
export const useCreateSubplebbit = (options?: UseCreateSubplebbitOptions): UseCreateSubplebbitResult => {
const result = useCreateCommunityBase(options);
return useMemo(
() => ({
...result,
createdSubplebbit: withSubplebbitAddress(result.createdCommunity),
createSubplebbit: result.createCommunity,
}),
[result],
);
};
export const usePublishSubplebbitEdit = (options?: UsePublishSubplebbitEditOptions): UsePublishSubplebbitEditResult => {
const { subplebbitAddress, communityAddress, ...rest } = options || {};
const result = usePublishCommunityEditBase({
communityAddress: communityAddress || subplebbitAddress,
...rest,
});
return useMemo(
() => ({
...result,
publishSubplebbitEdit: result.publishCommunityEdit,
}),
[result],
);
};
export const deleteSubplebbit = deleteCommunityBase;
export const setAccount = async (account: Account): Promise<void> => {
await setAccountBase(normalizeAccountForSet(account));
};
export const useAccountComment = (options?: UseAccountCommentOptions): UseAccountCommentResult => {
if (options?.commentIndex === undefined && options?.commentCid === undefined) {
return {
accountId: '',
index: -1,
state: 'initializing',
error: undefined,
errors: [],
refresh: async () => {},
};
}
const result = useAccountCommentBase(options);
return useMemo(
() => ({
...result,
refresh: async () => {},
}),
[result],
);
};

View File

@@ -0,0 +1,13 @@
/**
* Returns a short display-safe version of an address.
* If the address contains a dot (e.g. "my-community.eth"), it is returned as-is.
* Otherwise the middle bytes (characters 8-20) are returned as the shortened form.
*/
const getShortAddress = (address: string): string => {
if (!address) return '';
if (address.includes('.')) return address;
if (address.length < 20) return '';
return address.slice(8, 20);
};
export default getShortAddress;

View File

@@ -1,4 +1,5 @@
import { Comment, useSubplebbit } from '@bitsocial/bitsocial-react-hooks';
import { Comment, useCommunity } from '@bitsocial/bitsocial-react-hooks';
import { getCommunityIdentifier } from '../../hooks/use-community-identifier';
import React, { ReactNode, Fragment } from 'react';
/**
@@ -265,10 +266,8 @@ export const commentMatchesPattern = (comment: Comment, pattern: string, subpleb
* @returns True if the comment matches the pattern, false otherwise
*/
export const useCommentMatchesPattern = (comment: Comment, pattern: string): boolean => {
const subplebbit = useSubplebbit({
subplebbitAddress: comment?.subplebbitAddress,
onlyIfCached: true,
});
const communityIdentifier = getCommunityIdentifier(comment?.subplebbitAddress);
const subplebbit = useCommunity(communityIdentifier ? { community: communityIdentifier, onlyIfCached: true } : undefined);
return commentMatchesPattern(comment, pattern, subplebbit?.roles);
};

View File

@@ -1,4 +1,4 @@
import { Role, Subplebbit, Comment } from '@bitsocial/bitsocial-react-hooks';
import { Role, Community, Comment } from '@bitsocial/bitsocial-react-hooks';
export type Roles = { [address: string]: Role };
@@ -26,7 +26,7 @@ export const findSubplebbitCreator = (roles: Roles | undefined): string => {
return 'anonymous';
};
export const findAuthorSubplebbits = (authorAddress: string | undefined, subplebbits: (Subplebbit | undefined)[]): string[] => {
export const findAuthorSubplebbits = (authorAddress: string | undefined, subplebbits: (Community | undefined)[]): string[] => {
let authorSubplebbits: string[] = [];
if (!authorAddress || !subplebbits) {

View File

@@ -2,22 +2,22 @@ export type ParamsType = {
accountCommentIndex?: string;
authorAddress?: string;
commentCid?: string;
subplebbitAddress?: string;
communityAddress?: string;
timeFilterName?: string;
};
export type ViewType = 'home' | 'pending' | 'post' | 'submit' | 'subplebbit' | 'subplebbit/submit';
export type ViewType = 'home' | 'pending' | 'post' | 'submit' | 'community' | 'community/submit';
const sortTypes = ['/hot', '/new', '/active', '/topAll'];
export const getAboutLink = (pathname: string, params: ParamsType): string => {
// some subs might use emojis in their address, so we need to decode the pathname
// some communities might use emojis in their address, so we need to decode the pathname
const decodedPathname = decodeURIComponent(pathname);
if (decodedPathname.startsWith(`/s/${params.subplebbitAddress}/c/${params.commentCid}`)) {
return `/s/${params.subplebbitAddress}/c/${params.commentCid}/about`;
} else if (decodedPathname.startsWith(`/s/${params.subplebbitAddress}`)) {
return `/s/${params.subplebbitAddress}/about`;
if (decodedPathname.startsWith(`/s/${params.communityAddress}/c/${params.commentCid}`)) {
return `/s/${params.communityAddress}/c/${params.commentCid}/about`;
} else if (decodedPathname.startsWith(`/s/${params.communityAddress}`)) {
return `/s/${params.communityAddress}/about`;
} else if (decodedPathname.startsWith('/profile')) {
return '/profile/about';
} else if (decodedPathname.startsWith('/u/')) {
@@ -49,7 +49,7 @@ export const isAuthorSubmittedView = (pathname: string, params: ParamsType): boo
return pathname === `/u/${params.authorAddress}/c/${params.commentCid}/submitted`;
};
export const isCreateSubplebbitView = (pathname: string): boolean => {
export const isCreateCommunityView = (pathname: string): boolean => {
return pathname === '/communities/create';
};
@@ -98,20 +98,20 @@ export const isPendingPostView = (pathname: string, params: ParamsType): boolean
};
export const isPostPageView = (pathname: string, params: ParamsType): boolean => {
// some subs might use emojis in their address, so we need to decode the pathname
// some communities might use emojis in their address, so we need to decode the pathname
const decodedPathname = decodeURIComponent(pathname);
return params.subplebbitAddress && params.commentCid ? decodedPathname.startsWith(`/s/${params.subplebbitAddress}/c/${params.commentCid}`) : false;
return params.communityAddress && params.commentCid ? decodedPathname.startsWith(`/s/${params.communityAddress}/c/${params.commentCid}`) : false;
};
export const isPostPageAboutView = (pathname: string, params: ParamsType): boolean => {
return params.subplebbitAddress && params.commentCid ? pathname.startsWith(`/s/${params.subplebbitAddress}/c/${params.commentCid}/about`) : false;
return params.communityAddress && params.commentCid ? pathname.startsWith(`/s/${params.communityAddress}/c/${params.commentCid}/about`) : false;
};
export const isPostContextView = (pathname: string, params: ParamsType, search: string): boolean => {
if (!params.subplebbitAddress || !params.commentCid) return false;
if (!params.communityAddress || !params.commentCid) return false;
const decodedPathname = decodeURIComponent(pathname);
const expectedPathBase = `/s/${params.subplebbitAddress}/c/${params.commentCid}`;
const expectedPathBase = `/s/${params.communityAddress}/c/${params.commentCid}`;
if (!decodedPathname.startsWith(expectedPathBase)) return false;
@@ -165,52 +165,52 @@ export const isSubmitView = (pathname: string): boolean => {
return pathname.endsWith('/submit');
};
export const isSubplebbitView = (pathname: string, params: ParamsType): boolean => {
// some subs might use emojis in their address, so we need to decode the pathname
export const isCommunityView = (pathname: string, params: ParamsType): boolean => {
// some communities might use emojis in their address, so we need to decode the pathname
const decodedPathname = decodeURIComponent(pathname);
return params.subplebbitAddress ? decodedPathname.startsWith(`/s/${params.subplebbitAddress}`) : false;
return params.communityAddress ? decodedPathname.startsWith(`/s/${params.communityAddress}`) : false;
};
export const isSubplebbitAboutView = (pathname: string, params: ParamsType): boolean => {
return params.subplebbitAddress ? pathname.startsWith(`/s/${params.subplebbitAddress}/about`) : false;
export const isCommunityAboutView = (pathname: string, params: ParamsType): boolean => {
return params.communityAddress ? pathname.startsWith(`/s/${params.communityAddress}/about`) : false;
};
export const isSubplebbitSettingsView = (pathname: string, params: ParamsType): boolean => {
return params.subplebbitAddress ? pathname === `/s/${params.subplebbitAddress}/settings` || pathname === `/s/${params.subplebbitAddress}/settings/editor` : false;
export const isCommunitySettingsView = (pathname: string, params: ParamsType): boolean => {
return params.communityAddress ? pathname === `/s/${params.communityAddress}/settings` || pathname === `/s/${params.communityAddress}/settings/editor` : false;
};
export const isSubplebbitSubmitView = (pathname: string, params: ParamsType): boolean => {
return params.subplebbitAddress ? pathname === `/s/${params.subplebbitAddress}/submit` : false;
export const isCommunitySubmitView = (pathname: string, params: ParamsType): boolean => {
return params.communityAddress ? pathname === `/s/${params.communityAddress}/submit` : false;
};
export const isSubplebbitsView = (pathname: string): boolean => {
export const isCommunitiesView = (pathname: string): boolean => {
return pathname.startsWith('/communities');
};
export const isSubplebbitsSubscriberView = (pathname: string): boolean => {
export const isCommunitiesSubscriberView = (pathname: string): boolean => {
return pathname === '/communities/subscriber';
};
export const isSubplebbitsModeratorView = (pathname: string): boolean => {
export const isCommunitiesModeratorView = (pathname: string): boolean => {
return pathname === '/communities/moderator';
};
export const isSubplebbitsAdminView = (pathname: string): boolean => {
export const isCommunitiesAdminView = (pathname: string): boolean => {
return pathname === '/communities/admin';
};
export const isSubplebbitsOwnerView = (pathname: string): boolean => {
export const isCommunitiesOwnerView = (pathname: string): boolean => {
return pathname === '/communities/owner';
};
export const isSubplebbitsVoteView = (pathname: string): boolean => {
export const isCommunitiesVoteView = (pathname: string): boolean => {
return pathname.startsWith('/communities/vote');
};
export const isSubplebbitsVotePassingView = (pathname: string): boolean => {
export const isCommunitiesVotePassingView = (pathname: string): boolean => {
return pathname === '/communities/vote/passing';
};
export const isSubplebbitsVoteRejectingView = (pathname: string): boolean => {
export const isCommunitiesVoteRejectingView = (pathname: string): boolean => {
return pathname === '/communities/vote/rejecting';
};

View File

@@ -0,0 +1,42 @@
import { useEffect, useMemo } from 'react';
import { create } from 'zustand';
interface CommunitiesLoadingStartTimestampsState {
timestamps: Record<string, number>;
addCommunities: (communityAddresses: string[]) => void;
}
const useCommunitiesLoadingStartTimestampsStore = create<CommunitiesLoadingStartTimestampsState>((set, get) => ({
timestamps: {},
addCommunities: (communityAddresses) => {
const { timestamps } = get();
const newTimestamps: Record<string, number> = {};
communityAddresses.forEach((communityAddress) => {
if (!timestamps[communityAddress]) {
newTimestamps[communityAddress] = Math.round(Date.now() / 1000);
}
});
if (Object.keys(newTimestamps).length) {
set((state) => ({ timestamps: { ...state.timestamps, ...newTimestamps } }));
}
},
}));
const useCommunitiesLoadingStartTimestamps = (communityAddresses?: string[]) => {
const timestampsStore = useCommunitiesLoadingStartTimestampsStore((state) => state.timestamps);
const addCommunities = useCommunitiesLoadingStartTimestampsStore((state) => state.addCommunities);
useEffect(() => {
if (communityAddresses) {
addCommunities(communityAddresses);
}
}, [communityAddresses, addCommunities]);
const communitiesLoadingStartTimestamps = useMemo(() => {
return communityAddresses?.map((communityAddress) => timestampsStore[communityAddress]) || [];
}, [timestampsStore, communityAddresses]);
return communitiesLoadingStartTimestamps;
};
export default useCommunitiesLoadingStartTimestamps;

View File

@@ -0,0 +1,51 @@
import { create } from 'zustand';
interface CommunityOfflineState {
state?: string;
updatedAt?: number;
updatingState?: string;
initialLoad: boolean;
}
interface CommunityOfflineStore {
communityOfflineState: Record<string, CommunityOfflineState>;
setCommunityOfflineState: (address: string, state: Partial<CommunityOfflineState>) => void;
initializeCommunityOfflineState: (address: string) => void;
}
const useCommunityOfflineStore = create<CommunityOfflineStore>((set) => ({
communityOfflineState: {},
setCommunityOfflineState: (address, newState) =>
set((state) => ({
communityOfflineState: {
...state.communityOfflineState,
[address]: {
...state.communityOfflineState[address],
...newState,
},
},
})),
initializeCommunityOfflineState: (address) => {
set((state) => ({
communityOfflineState: {
...state.communityOfflineState,
[address]: {
initialLoad: true,
},
},
}));
setTimeout(() => {
set((state) => ({
communityOfflineState: {
...state.communityOfflineState,
[address]: {
...state.communityOfflineState[address],
initialLoad: false,
},
},
}));
}, 30000);
},
}));
export default useCommunityOfflineStore;

View File

@@ -1,8 +1,8 @@
import { PublishSubplebbitEditOptions } from '@bitsocial/bitsocial-react-hooks';
import { PublishCommunityEditOptions } from '@bitsocial/bitsocial-react-hooks';
import { Roles } from '../lib/utils/user-utils';
import { create } from 'zustand';
export type SubplebbitSettingsState = {
export type CommunitySettingsState = {
challenges: any[] | undefined;
title: string | undefined;
description: string | undefined;
@@ -11,13 +11,13 @@ export type SubplebbitSettingsState = {
rules: string[] | undefined;
roles: Roles | undefined;
settings: any | undefined;
subplebbitAddress: string | undefined;
publishSubplebbitEditOptions: PublishSubplebbitEditOptions;
setSubplebbitSettingsStore: (data: Partial<SubplebbitSettingsState>) => void;
resetSubplebbitSettingsStore: () => void;
communityAddress: string | undefined;
publishCommunityEditOptions: PublishCommunityEditOptions;
setCommunitySettingsStore: (data: Partial<CommunitySettingsState>) => void;
resetCommunitySettingsStore: () => void;
};
const useSubplebbitSettingsStore = create<SubplebbitSettingsState>((set) => ({
const useCommunitySettingsStore = create<CommunitySettingsState>((set) => ({
challenges: undefined,
title: undefined,
description: undefined,
@@ -26,9 +26,9 @@ const useSubplebbitSettingsStore = create<SubplebbitSettingsState>((set) => ({
rules: undefined,
roles: undefined,
settings: undefined,
subplebbitAddress: undefined,
publishSubplebbitEditOptions: {},
setSubplebbitSettingsStore: (props) =>
communityAddress: undefined,
publishCommunityEditOptions: {},
setCommunitySettingsStore: (props) =>
set((state) => {
const nextState = { ...state };
Object.entries(props).forEach(([key, value]) => {
@@ -36,7 +36,7 @@ const useSubplebbitSettingsStore = create<SubplebbitSettingsState>((set) => ({
(nextState as any)[key] = value;
}
});
const editOptions: Partial<SubplebbitSettingsState> = {};
const editOptions: Partial<CommunitySettingsState> = {};
if (nextState.title !== undefined) editOptions.title = nextState.title?.trim() === '' ? undefined : nextState.title;
if (nextState.description !== undefined) editOptions.description = nextState.description?.trim() === '' ? undefined : nextState.description;
if (nextState.address !== undefined) editOptions.address = nextState.address?.trim() === '' ? undefined : nextState.address;
@@ -44,11 +44,11 @@ const useSubplebbitSettingsStore = create<SubplebbitSettingsState>((set) => ({
if (nextState.rules !== undefined) editOptions.rules = nextState.rules;
if (nextState.roles !== undefined) editOptions.roles = nextState.roles;
if (nextState.settings !== undefined) editOptions.settings = nextState.settings;
if (nextState.subplebbitAddress !== undefined) editOptions.subplebbitAddress = nextState.subplebbitAddress?.trim() === '' ? undefined : nextState.subplebbitAddress;
nextState.publishSubplebbitEditOptions = editOptions;
if (nextState.communityAddress !== undefined) editOptions.communityAddress = nextState.communityAddress?.trim() === '' ? undefined : nextState.communityAddress;
nextState.publishCommunityEditOptions = editOptions;
return nextState;
}),
resetSubplebbitSettingsStore: () =>
resetCommunitySettingsStore: () =>
set(() => {
return {
challenges: undefined,
@@ -59,10 +59,10 @@ const useSubplebbitSettingsStore = create<SubplebbitSettingsState>((set) => ({
rules: undefined,
roles: undefined,
settings: undefined,
subplebbitAddress: undefined,
publishSubplebbitEditOptions: {},
communityAddress: undefined,
publishCommunityEditOptions: {},
};
}),
}));
export default useSubplebbitSettingsStore;
export default useCommunitySettingsStore;

View File

@@ -1,51 +0,0 @@
import { create } from 'zustand';
interface SubplebbitOfflineState {
state?: string;
updatedAt?: number;
updatingState?: string;
initialLoad: boolean;
}
interface SubplebbitOfflineStore {
subplebbitOfflineState: Record<string, SubplebbitOfflineState>;
setSubplebbitOfflineState: (address: string, state: Partial<SubplebbitOfflineState>) => void;
initializesubplebbitOfflineState: (address: string) => void;
}
const useSubplebbitOfflineStore = create<SubplebbitOfflineStore>((set) => ({
subplebbitOfflineState: {},
setSubplebbitOfflineState: (address, newState) =>
set((state) => ({
subplebbitOfflineState: {
...state.subplebbitOfflineState,
[address]: {
...state.subplebbitOfflineState[address],
...newState,
},
},
})),
initializesubplebbitOfflineState: (address) => {
set((state) => ({
subplebbitOfflineState: {
...state.subplebbitOfflineState,
[address]: {
initialLoad: true,
},
},
}));
setTimeout(() => {
set((state) => ({
subplebbitOfflineState: {
...state.subplebbitOfflineState,
[address]: {
...state.subplebbitOfflineState[address],
initialLoad: false,
},
},
}));
}, 30000);
},
}));
export default useSubplebbitOfflineStore;

View File

@@ -1,42 +0,0 @@
import { useEffect, useMemo } from 'react';
import { create } from 'zustand';
interface SubplebbitsLoadingStartTimestampsState {
timestamps: Record<string, number>;
addSubplebbits: (subplebbitAddresses: string[]) => void;
}
const useSubplebbitsLoadingStartTimestampsStore = create<SubplebbitsLoadingStartTimestampsState>((set, get) => ({
timestamps: {},
addSubplebbits: (subplebbitAddresses) => {
const { timestamps } = get();
const newTimestamps: Record<string, number> = {};
subplebbitAddresses.forEach((subplebbitAddress) => {
if (!timestamps[subplebbitAddress]) {
newTimestamps[subplebbitAddress] = Math.round(Date.now() / 1000);
}
});
if (Object.keys(newTimestamps).length) {
set((state) => ({ timestamps: { ...state.timestamps, ...newTimestamps } }));
}
},
}));
const useSubplebbitsLoadingStartTimestamps = (subplebbitAddresses?: string[]) => {
const timestampsStore = useSubplebbitsLoadingStartTimestampsStore((state) => state.timestamps);
const addSubplebbits = useSubplebbitsLoadingStartTimestampsStore((state) => state.addSubplebbits);
useEffect(() => {
if (subplebbitAddresses) {
addSubplebbits(subplebbitAddresses);
}
}, [subplebbitAddresses, addSubplebbits]);
const subplebbitsLoadingStartTimestamps = useMemo(() => {
return subplebbitAddresses?.map((subplebbitAddress) => timestampsStore[subplebbitAddress]) || [];
}, [timestampsStore, subplebbitAddresses]);
return subplebbitsLoadingStartTimestamps;
};
export default useSubplebbitsLoadingStartTimestamps;

View File

@@ -3,7 +3,7 @@ import { HashLink } from 'react-router-hash-link';
import useIsMobile from '../../hooks/use-is-mobile';
import Sidebar, { Footer } from '../../components/sidebar';
import styles from './about.module.css';
import { useAccount, useComment, useSubplebbit } from '@bitsocial/bitsocial-react-hooks';
import { useAccount, useComment, useCommunity } from '@bitsocial/bitsocial-react-hooks';
import { Capacitor } from '@capacitor/core';
import { isHomeAboutView } from '../../lib/utils/view-utils';
import { useEffect } from 'react';
@@ -156,9 +156,9 @@ const About = () => {
const location = useLocation();
const navigate = useNavigate();
const isInHomeAboutView = isHomeAboutView(location.pathname);
const { commentCid, subplebbitAddress } = useParams();
const { commentCid, communityAddress: subplebbitAddress } = useParams();
const subplebbit = useSubplebbit({ subplebbitAddress });
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress } } : undefined);
const comment = useComment({ commentCid: commentCid as string, onlyIfCached: true });
useEffect(() => {

View File

@@ -8,6 +8,7 @@ import useFeedFiltersStore from '../../stores/use-feed-filters-store';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter';
import FeedFooter from '../../components/feed-footer';
import { getCommunityIdentifiers } from '../../hooks/use-community-identifier';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Post from '../../components/post';
import Sidebar from '../../components/sidebar';
@@ -54,7 +55,7 @@ const All = () => {
const options: any = {
newerThan: searchQuery ? 0 : timeFilterSeconds,
sortType,
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
};
if (searchQuery) {
@@ -70,7 +71,7 @@ const All = () => {
return options;
}, [subplebbitAddresses, sortType, timeFilterSeconds, searchQuery]);
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
const { feed, hasMore, loadMore, reset, communityKeysWithNewerPosts: subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
// Reset no results state when search query changes
useEffect(() => {
@@ -105,7 +106,7 @@ const All = () => {
hasMore: hasMoreWeekly,
loadMore: loadMoreWeekly,
} = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 7,
});
@@ -114,7 +115,7 @@ const All = () => {
hasMore: hasMoreMonthly,
loadMore: loadMoreMonthly,
} = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 30,
});
@@ -123,7 +124,7 @@ const All = () => {
hasMore: hasMoreYearly,
loadMore: loadMoreYearly,
} = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 365,
});

View File

@@ -1,24 +1,25 @@
import { Fragment, useEffect, useMemo, useState, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { Subplebbit as SubplebbitType, useAccount, useAccountSubplebbits, useSubplebbits, useSubplebbitStats } from '@bitsocial/bitsocial-react-hooks';
import styles from './subplebbits.module.css';
import { Community as CommunityType, useAccount, useAccountCommunities, useCommunities, useCommunityStats } from '@bitsocial/bitsocial-react-hooks';
import styles from './communities.module.css';
import { getFormattedTimeDuration } from '../../lib/utils/time-utils';
import {
isSubplebbitsView,
isSubplebbitsSubscriberView,
isSubplebbitsModeratorView,
isSubplebbitsAdminView,
isSubplebbitsOwnerView,
isSubplebbitsVoteView,
isSubplebbitsVotePassingView,
isSubplebbitsVoteRejectingView,
isCommunitiesView,
isCommunitiesSubscriberView,
isCommunitiesModeratorView,
isCommunitiesAdminView,
isCommunitiesOwnerView,
isCommunitiesVoteView,
isCommunitiesVotePassingView,
isCommunitiesVoteRejectingView,
} from '../../lib/utils/view-utils';
import useErrorStore from '../../stores/use-error-store';
import { getCommunityIdentifier, getCommunityIdentifiers } from '../../hooks/use-community-identifier';
import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import useDisplayedSubscriptions from '../../hooks/use-displayed-subscriptions';
import useIsMobile from '../../hooks/use-is-mobile';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
import useIsCommunityOffline from '../../hooks/use-is-community-offline';
import ErrorDisplay from '../../components/error-display';
import Markdown from '../../components/markdown';
import Label from '../../components/post/label';
@@ -29,7 +30,7 @@ import _ from 'lodash';
interface SubplebbitProps {
index?: number;
subplebbit: SubplebbitType;
subplebbit: CommunityType;
tags?: string[];
isUnsubscribed?: boolean;
onUnsubscribe?: (address: string) => void;
@@ -43,32 +44,32 @@ const NoCommunitiesMessage = () => {
const MyCommunitiesTabs = () => {
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 isInSubplebbitsView =
isSubplebbitsView(location.pathname) && !isInSubplebbitsSubscriberView && !isInSubplebbitsModeratorView && !isInSubplebbitsAdminView && !isInSubplebbitsOwnerView;
const isInCommunitiesSubscriberView = isCommunitiesSubscriberView(location.pathname);
const isInCommunitiesModeratorView = isCommunitiesModeratorView(location.pathname);
const isInCommunitiesAdminView = isCommunitiesAdminView(location.pathname);
const isInCommunitiesOwnerView = isCommunitiesOwnerView(location.pathname);
const isInCommunitiesView =
isCommunitiesView(location.pathname) && !isInCommunitiesSubscriberView && !isInCommunitiesModeratorView && !isInCommunitiesAdminView && !isInCommunitiesOwnerView;
return (
<div className={styles.subplebbitsTabs}>
<Link to='/communities' className={isInSubplebbitsView ? styles.selected : styles.choice}>
<Link to='/communities' className={isInCommunitiesView ? styles.selected : styles.choice}>
{t('all')}
</Link>
<span className={styles.separator}>|</span>
<Link to='/communities/subscriber' className={isInSubplebbitsSubscriberView ? styles.selected : styles.choice}>
<Link to='/communities/subscriber' className={isInCommunitiesSubscriberView ? styles.selected : styles.choice}>
{t('subscriber')}
</Link>
<span className={styles.separator}>|</span>
<Link to='/communities/moderator' className={isInSubplebbitsModeratorView ? styles.selected : styles.choice}>
<Link to='/communities/moderator' className={isInCommunitiesModeratorView ? styles.selected : styles.choice}>
{t('moderator')}
</Link>
<span className={styles.separator}>|</span>
<Link to='/communities/admin' className={isInSubplebbitsAdminView ? styles.selected : styles.choice}>
<Link to='/communities/admin' className={isInCommunitiesAdminView ? styles.selected : styles.choice}>
{t('admin')}
</Link>
<span className={styles.separator}>|</span>
<Link to='/communities/owner' className={isInSubplebbitsOwnerView ? styles.selected : styles.choice}>
<Link to='/communities/owner' className={isInCommunitiesOwnerView ? styles.selected : styles.choice}>
{t('owner')}
</Link>
</div>
@@ -78,19 +79,19 @@ const MyCommunitiesTabs = () => {
const VoteTabs = () => {
const { t } = useTranslation();
const location = useLocation();
const isInSubplebbitsVoteView = isSubplebbitsVoteView(location.pathname);
const isInSubplebbitsVotePassingView = isSubplebbitsVotePassingView(location.pathname);
const isInSubplebbitsVoteRejectingView = isSubplebbitsVoteRejectingView(location.pathname);
const isInCommunitiesVoteView = isCommunitiesVoteView(location.pathname);
const isInCommunitiesVotePassingView = isCommunitiesVotePassingView(location.pathname);
const isInCommunitiesVoteRejectingView = isCommunitiesVoteRejectingView(location.pathname);
return (
<div className={styles.subplebbitsTabs}>
<Link to='/communities/vote' className={isInSubplebbitsVoteView ? styles.selected : styles.choice}>
<Link to='/communities/vote' className={isInCommunitiesVoteView ? styles.selected : styles.choice}>
{t('all')}
</Link>
<span className={styles.separator}>|</span>
<Link
to='/communities/vote/passing'
className={isInSubplebbitsVotePassingView ? styles.selected : styles.choice}
className={isInCommunitiesVotePassingView ? styles.selected : styles.choice}
onClick={(e) => {
e.preventDefault();
alert('This feature is not available yet.');
@@ -102,7 +103,7 @@ const VoteTabs = () => {
<span className={styles.separator}>|</span>
<Link
to='/communities/vote/rejecting'
className={isInSubplebbitsVoteRejectingView ? styles.selected : styles.choice}
className={isInCommunitiesVoteRejectingView ? styles.selected : styles.choice}
onClick={(e) => {
e.preventDefault();
alert('This feature is not available yet.');
@@ -117,21 +118,21 @@ const VoteTabs = () => {
const Infobar = () => {
const account = useAccount();
const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits();
const { accountCommunities: accountSubplebbits, error: accountCommunitiesError } = useAccountCommunities();
const { setError } = useErrorStore();
useEffect(() => {
setError('Infobar_useAccountSubplebbits', accountSubplebbitsError);
}, [accountSubplebbitsError, setError]);
setError('Infobar_useAccountSubplebbits', accountCommunitiesError);
}, [accountCommunitiesError, setError]);
const subscriptions = account?.subscriptions || [];
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 isInCommunitiesSubscriberView = isCommunitiesSubscriberView(location.pathname);
const isInCommunitiesModeratorView = isCommunitiesModeratorView(location.pathname);
const isInCommunitiesAdminView = isCommunitiesAdminView(location.pathname);
const isInCommunitiesOwnerView = isCommunitiesOwnerView(location.pathname);
// Check if we're filtering by any tag
const urlParams = new URLSearchParams(location.search);
@@ -141,9 +142,9 @@ const Infobar = () => {
const basePath = location.pathname;
let mainInfobarText;
if (isInSubplebbitsSubscriberView) {
if (isInCommunitiesSubscriberView) {
mainInfobarText = subscriptions.length === 0 ? t('not_subscribed') : t('below_subscribed');
} else if (isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView) {
} else if (isInCommunitiesModeratorView || isInCommunitiesAdminView || isInCommunitiesOwnerView) {
mainInfobarText = Object.keys(accountSubplebbits).length > 0 ? t('below_moderator_access') : t('not_moderator');
} else if (subscriptions.length === 0 && Object.keys(accountSubplebbits).length === 0) {
mainInfobarText = t('not_subscriber_nor_moderator');
@@ -170,7 +171,7 @@ const Infobar = () => {
);
};
const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: SubplebbitProps) => {
const CommunityItem = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: SubplebbitProps) => {
const { t } = useTranslation();
const { address, createdAt, description, roles, shortAddress, settings, suggested, title } = subplebbit || {};
const [avatarLoadFailed, setAvatarLoadFailed] = useState(false);
@@ -198,8 +199,8 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }:
const downvoteCount = 0;
const postScore = upvoteCount === 0 && downvoteCount === 0 ? '•' : upvoteCount - downvoteCount || '•';
const { allActiveUserCount } = useSubplebbitStats({ subplebbitAddress: address });
const { isOffline, isOnlineStatusLoading, offlineTitle } = useIsSubplebbitOffline(subplebbit);
const { allActiveUserCount } = useCommunityStats(address ? { community: getCommunityIdentifier(address) } : undefined);
const { isOffline, isOnlineStatusLoading, offlineTitle } = useIsCommunityOffline(subplebbit);
const isNsfw = tags?.some((tag) => nsfwTags.includes(tag));
@@ -310,14 +311,14 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }:
const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => {
const account = useAccount();
const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits();
const { accountCommunities: accountSubplebbits, error: accountCommunitiesError } = useAccountCommunities();
const { setError } = useErrorStore();
const location = useLocation();
const defaultSubplebbits = useDefaultSubplebbits();
useEffect(() => {
setError('AccountSubplebbits_useAccountSubplebbits', accountSubplebbitsError);
}, [accountSubplebbitsError, setError, viewRole]);
setError('AccountSubplebbits_useAccountSubplebbits', accountCommunitiesError);
}, [accountCommunitiesError, setError, viewRole]);
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
@@ -342,7 +343,7 @@ const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => {
})
.map((subplebbitData, index) => {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return <Subplebbit key={index} subplebbit={subplebbitData} tags={tags} index={index} />;
return <CommunityItem key={index} subplebbit={subplebbitData} tags={tags} index={index} />;
});
if (subplebbitElements.length === 0) {
@@ -373,14 +374,14 @@ const SubscriberSubplebbits = () => {
[account?.author?.address], // Reset dependencies
);
const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses: displayedSubscriptions });
const { communities: subplebbits, error: subplebbitsError } = useCommunities({ communities: getCommunityIdentifiers(displayedSubscriptions) });
useEffect(() => {
setError('SubscriberSubplebbits_useSubplebbits', subplebbitsError);
}, [subplebbitsError, setError]);
const subplebbitElements = Object.values(subplebbits ?? {})
.filter((subplebbit): subplebbit is SubplebbitType => Boolean(subplebbit))
.filter((subplebbit): subplebbit is CommunityType => Boolean(subplebbit))
.filter((subplebbitData) => {
if (currentTag) {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
@@ -396,7 +397,7 @@ const SubscriberSubplebbits = () => {
.map((subplebbitData, index) => {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return subplebbitData ? (
<Subplebbit
<CommunityItem
key={subplebbitData.address || index}
subplebbit={subplebbitData}
tags={tags}
@@ -422,7 +423,7 @@ const AllDefaultSubplebbits = () => {
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses });
const { communities: subplebbits, error: subplebbitsError } = useCommunities({ communities: getCommunityIdentifiers(subplebbitAddresses) });
const { setError } = useErrorStore();
useEffect(() => {
@@ -430,7 +431,7 @@ const AllDefaultSubplebbits = () => {
}, [subplebbitsError, setError]);
const subplebbitElements = Object.values(subplebbits ?? {})
.filter((subplebbit): subplebbit is SubplebbitType => Boolean(subplebbit)) // Type guard
.filter((subplebbit): subplebbit is CommunityType => Boolean(subplebbit)) // Type guard
.filter((subplebbitData) => {
if (currentTag) {
const tags = defaultSubplebbitsList.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
@@ -445,7 +446,7 @@ const AllDefaultSubplebbits = () => {
})
.map((subplebbitData, index) => {
const tags = defaultSubplebbitsList.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return <Subplebbit key={subplebbitData.address || index} subplebbit={subplebbitData} tags={tags} index={index} />;
return <CommunityItem key={subplebbitData.address || index} subplebbit={subplebbitData} tags={tags} index={index} />;
});
if (subplebbitElements.length === 0) {
@@ -456,14 +457,14 @@ const AllDefaultSubplebbits = () => {
const AllAccountSubplebbits = () => {
const account = useAccount();
const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits();
const { accountCommunities: accountSubplebbits, error: accountCommunitiesError } = useAccountCommunities();
const { setError } = useErrorStore();
const location = useLocation();
const defaultSubplebbits = useDefaultSubplebbits();
useEffect(() => {
setError('AllAccountSubplebbits_useAccountSubplebbits', accountSubplebbitsError);
}, [accountSubplebbitsError, setError]);
setError('AllAccountSubplebbits_useAccountSubplebbits', accountCommunitiesError);
}, [accountCommunitiesError, setError]);
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
@@ -476,14 +477,14 @@ const AllAccountSubplebbits = () => {
const { list: displayedAddresses, isUnsubscribed, handleUnsubscribe } = useDisplayedSubscriptions(getAllAccountRelatedAddresses, [account?.author?.address]);
const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses: displayedAddresses });
const { communities: subplebbits, error: subplebbitsError } = useCommunities({ communities: getCommunityIdentifiers(displayedAddresses) });
useEffect(() => {
setError('AllAccountSubplebbits_useSubplebbits', subplebbitsError);
}, [subplebbitsError, setError]);
const subplebbitElements = Object.values(subplebbits ?? {})
.filter((subplebbit): subplebbit is SubplebbitType => Boolean(subplebbit))
.filter((subplebbit): subplebbit is CommunityType => Boolean(subplebbit))
.filter((subplebbitData) => {
if (currentTag) {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
@@ -499,7 +500,7 @@ const AllAccountSubplebbits = () => {
.map((subplebbitData, index) => {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return subplebbitData ? (
<Subplebbit
<CommunityItem
key={subplebbitData.address || index}
subplebbit={subplebbitData}
tags={tags}
@@ -517,7 +518,7 @@ const AllAccountSubplebbits = () => {
return <>{subplebbitElements}</>;
};
const Subplebbits = () => {
const Communities = () => {
const { t } = useTranslation();
const location = useLocation();
const { errors, clearAllErrors } = useErrorStore();
@@ -536,45 +537,45 @@ const Subplebbits = () => {
});
}, [errors]);
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;
let viewRole = 'subscriber';
if (isInSubplebbitsModeratorView) {
if (isInCommunitiesModeratorView) {
viewRole = 'moderator';
} else if (isInSubplebbitsAdminView) {
} else if (isInCommunitiesAdminView) {
viewRole = 'admin';
} else if (isInSubplebbitsOwnerView) {
} else if (isInCommunitiesOwnerView) {
viewRole = 'owner';
}
const documentTitle = useMemo(() => {
let title = t('communities').charAt(0).toUpperCase() + t('communities').slice(1);
if (isInSubplebbitsVoteView) {
if (isInCommunitiesVoteView) {
title += ` - ${_.startCase(t('vote'))}`;
} else if (isInSubplebbitsSubscriberView) {
} else if (isInCommunitiesSubscriberView) {
title += ` - ${_.startCase(t('subscriber'))}`;
} else if (isInSubplebbitsModeratorView) {
} else if (isInCommunitiesModeratorView) {
title += ` - ${_.startCase(t('moderator'))}`;
} else if (isInSubplebbitsAdminView) {
} else if (isInCommunitiesAdminView) {
title += ` - ${_.startCase(t('admin'))}`;
} else if (isInSubplebbitsOwnerView) {
} else if (isInCommunitiesOwnerView) {
title += ` - ${_.startCase(t('owner'))}`;
} else if (isInSubplebbitsView) {
} else if (isInCommunitiesView) {
title += ` - ${_.startCase(t('all'))}`;
}
return `${title} - Seedit`;
}, [isInSubplebbitsSubscriberView, isInSubplebbitsModeratorView, isInSubplebbitsAdminView, isInSubplebbitsOwnerView, isInSubplebbitsView, isInSubplebbitsVoteView, t]);
}, [isInCommunitiesSubscriberView, isInCommunitiesModeratorView, isInCommunitiesAdminView, isInCommunitiesOwnerView, isInCommunitiesView, isInCommunitiesVoteView, t]);
useEffect(() => {
document.title = documentTitle;
@@ -587,18 +588,18 @@ const Subplebbits = () => {
if (
source === 'Infobar_useAccountSubplebbits' &&
(isInSubplebbitsView || isInSubplebbitsSubscriberView || isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView)
(isInCommunitiesView || isInCommunitiesSubscriberView || isInCommunitiesModeratorView || isInCommunitiesAdminView || isInCommunitiesOwnerView)
) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AccountSubplebbits_useAccountSubplebbits' && (isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView)) {
} else if (source === 'AccountSubplebbits_useAccountSubplebbits' && (isInCommunitiesModeratorView || isInCommunitiesAdminView || isInCommunitiesOwnerView)) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'SubscriberSubplebbits_useSubplebbits' && isInSubplebbitsSubscriberView) {
} else if (source === 'SubscriberSubplebbits_useSubplebbits' && isInCommunitiesSubscriberView) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AllDefaultSubplebbits_useSubplebbits' && isInSubplebbitsVoteView) {
} else if (source === 'AllDefaultSubplebbits_useSubplebbits' && isInCommunitiesVoteView) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AllAccountSubplebbits_useAccountSubplebbits' && isInSubplebbitsView) {
} else if (source === 'AllAccountSubplebbits_useAccountSubplebbits' && isInCommunitiesView) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AllAccountSubplebbits_useSubplebbits' && isInSubplebbitsView) {
} else if (source === 'AllAccountSubplebbits_useSubplebbits' && isInCommunitiesView) {
// Avoid duplicate key if both errors from AllAccountSubplebbits are present
errorsToDisplay.push(<ErrorDisplay key={`${source}_subplebbits`} error={errorObj} />);
}
@@ -611,19 +612,19 @@ const Subplebbits = () => {
<div className={styles.sidebar}>
<Sidebar />
</div>
{isInSubplebbitsSubscriberView || isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView || isInSubplebbitsView ? (
{isInCommunitiesSubscriberView || isInCommunitiesModeratorView || isInCommunitiesAdminView || isInCommunitiesOwnerView || isInCommunitiesView ? (
<MyCommunitiesTabs />
) : (
<VoteTabs />
)}
<Infobar />
<div className={styles.error}>{renderErrors()}</div>
{isInSubplebbitsVoteView && <AllDefaultSubplebbits />}
{(isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView) && <AccountSubplebbits viewRole={viewRole} />}
{isInSubplebbitsSubscriberView && <SubscriberSubplebbits />}
{isInSubplebbitsView && <AllAccountSubplebbits />}
{isInCommunitiesVoteView && <AllDefaultSubplebbits />}
{(isInCommunitiesModeratorView || isInCommunitiesAdminView || isInCommunitiesOwnerView) && <AccountSubplebbits viewRole={viewRole} />}
{isInCommunitiesSubscriberView && <SubscriberSubplebbits />}
{isInCommunitiesView && <AllAccountSubplebbits />}
</div>
);
};
export default Subplebbits;
export default Communities;

View File

@@ -0,0 +1 @@
export { default } from './communities';

View File

@@ -1,16 +1,16 @@
import { useEffect, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { isCreateSubplebbitView } from '../../../lib/utils/view-utils';
import useSubplebbitSettingsStore, { SubplebbitSettingsState } from '../../../stores/use-subplebbit-settings-store';
import { isCreateCommunityView } from '../../../lib/utils/view-utils';
import useCommunitySettingsStore, { CommunitySettingsState } from '../../../stores/use-community-settings-store';
import useChallengesOptions from '../../../hooks/use-challenges-options';
import styles from '../subplebbit-settings.module.css';
import styles from '../community-settings.module.css';
interface ChallengeSettingsProps {
challenge: any;
index: number;
isReadOnly: boolean;
setSubplebbitSettingsStore: (data: Partial<SubplebbitSettingsState>) => void;
setCommunitySettingsStore: (data: Partial<CommunitySettingsState>) => void;
settings: any;
showSettings: boolean;
challengesSettings: any;
@@ -72,7 +72,7 @@ const ExcludeAddressesFromChallengeInput = ({
);
};
const ChallengeSettings = ({ challenge, challengesSettings, index, isReadOnly, setSubplebbitSettingsStore, settings, showSettings }: ChallengeSettingsProps) => {
const ChallengeSettings = ({ challenge, challengesSettings, index, isReadOnly, setCommunitySettingsStore, settings, showSettings }: ChallengeSettingsProps) => {
const { name, options } = challenge || {};
const challengeSettings = challengesSettings[name];
const readOnlyFallback = isReadOnly && Object.keys(challengeSettings || {}).length === 0 && challenge;
@@ -82,18 +82,18 @@ const ChallengeSettings = ({ challenge, challengesSettings, index, isReadOnly, s
const handleOptionChange = (optionName: string, newValue: string) => {
const updatedOptions = { ...options, [optionName]: newValue };
const updatedChallenges = settings.challenges?.map((ch: any, idx: number) => (idx === index ? { ...ch, options: updatedOptions } : ch));
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
};
const addExcludeGroup = () => {
const newExclude = { role: [], post: false, reply: false, vote: false };
if (challenge?.exclude) {
const updatedChallenges = settings?.challenges?.map((ch: any, idx: number) => (idx === index ? { ...ch, exclude: [...ch.exclude, newExclude] } : ch));
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setShowExcludeSettings((oldShowExcludeSettings) => [...oldShowExcludeSettings, true]);
} else {
const updatedChallenges = settings?.challenges?.map((ch: any, idx: number) => (idx === index ? { ...ch, exclude: [newExclude] } : ch));
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setShowExcludeSettings([true]);
}
};
@@ -102,7 +102,7 @@ const ChallengeSettings = ({ challenge, challengesSettings, index, isReadOnly, s
const updatedChallenges = [...settings.challenges];
const currentChallenge = updatedChallenges[index];
currentChallenge.exclude.splice(excludeIndex, 1);
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
};
const [showExcludeSettings, setShowExcludeSettings] = useState<boolean[]>([]);
@@ -153,7 +153,7 @@ const ChallengeSettings = ({ challenge, challengesSettings, index, isReadOnly, s
return ch;
});
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
};
const handleExcludeAddress = (excludeIndex: number, value: string) => {
@@ -206,7 +206,7 @@ const ChallengeSettings = ({ challenge, challengesSettings, index, isReadOnly, s
{!isReadOnly && <span className={styles.deleteButton} onClick={() => deleteExcludeGroup(excludeIndex)} title='delete group' />}
{!isReadOnly && (
<button className={styles.hideCombo} onClick={() => toggleExcludeSettings(excludeIndex)} disabled={isReadOnly}>
{showExcludeSettings?.[excludeIndex] ?? false ? 'Hide' : 'Show'} Group Settings
{(showExcludeSettings?.[excludeIndex] ?? false) ? 'Hide' : 'Show'} Group Settings
</button>
)}
{showExcludeSettings[excludeIndex] && (
@@ -426,29 +426,29 @@ const Challenges = ({
challengesSettings: any;
}) => {
const { t } = useTranslation();
const { settings, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { settings, setCommunitySettingsStore } = useCommunitySettingsStore();
const location = useLocation();
const isInCreateSubplebbitView = isCreateSubplebbitView(location.pathname);
const isInCreateCommunityView = isCreateCommunityView(location.pathname);
const challenges = settings?.challenges || readOnlyChallenges || [];
const [showSettings, setShowSettings] = useState<boolean[]>(challenges?.map(() => false));
const challengeOptions = useChallengesOptions();
const hasSetDefaultChallenge = useRef(false);
const valuesRef = useRef({ settings, setSubplebbitSettingsStore });
const valuesRef = useRef({ settings, setCommunitySettingsStore });
useEffect(() => {
valuesRef.current = { settings, setSubplebbitSettingsStore };
valuesRef.current = { settings, setCommunitySettingsStore };
});
useEffect(() => {
if (isInCreateSubplebbitView && !hasSetDefaultChallenge.current && challengeOptions && challengesSettings && !valuesRef.current.settings?.challenges?.length) {
if (isInCreateCommunityView && !hasSetDefaultChallenge.current && challengeOptions && challengesSettings && !valuesRef.current.settings?.challenges?.length) {
const defaultChallengeName = challengesSettings?.['captcha-canvas-v3'] ? 'captcha-canvas-v3' : challengeNames?.[0] || Object.keys(challengeOptions)[0];
const defaultChallenge = {
name: defaultChallengeName,
options: challengeOptions[defaultChallengeName] || {},
};
valuesRef.current.setSubplebbitSettingsStore({
valuesRef.current.setCommunitySettingsStore({
settings: {
...valuesRef.current.settings,
challenges: [defaultChallenge],
@@ -457,7 +457,7 @@ const Challenges = ({
hasSetDefaultChallenge.current = true;
}
}, [isInCreateSubplebbitView, challengeOptions, challengesSettings, challengeNames]);
}, [isInCreateCommunityView, challengeOptions, challengesSettings, challengeNames]);
const toggleSettings = (index: number) => {
const newShowSettings = [...showSettings];
@@ -472,13 +472,13 @@ const Challenges = ({
options: defaultOptions,
};
const updatedChallenges = [...(settings?.challenges || []), newChallenge];
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setShowSettings((oldShowSettings) => [...oldShowSettings, true]);
};
const handleDeleteChallenge = (index: number) => {
const updatedChallenges = settings?.challenges?.filter((_: any, idx: number) => idx !== index);
setSubplebbitSettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setCommunitySettingsStore({ settings: { ...settings, challenges: updatedChallenges } });
setShowSettings((oldShowSettings) => oldShowSettings.filter((_, idx) => idx !== index));
};
@@ -488,7 +488,7 @@ const Challenges = ({
const updatedChallenges = [...currentChallenges];
updatedChallenges[index] = { ...updatedChallenges[index], name: newType, options };
setSubplebbitSettingsStore({
setCommunitySettingsStore({
settings: {
...settings,
challenges: updatedChallenges,
@@ -506,7 +506,7 @@ const Challenges = ({
{t('add_a_challenge')}
</button>
)}
{challenges.length === 0 && !isInCreateSubplebbitView && <span className={styles.noChallengeWarning}>{t('warning_spam')}</span>}
{challenges.length === 0 && !isInCreateCommunityView && <span className={styles.noChallengeWarning}>{t('warning_spam')}</span>}
{challenges?.map((challenge: any, index: number) => (
<div key={index} className={styles.challenge}>
Challenge #{index + 1}
@@ -533,7 +533,7 @@ const Challenges = ({
challengesSettings={challengesSettings || {}}
index={index}
isReadOnly={isReadOnly}
setSubplebbitSettingsStore={setSubplebbitSettingsStore}
setCommunitySettingsStore={setCommunitySettingsStore}
settings={settings}
showSettings={showSettings[index]}
/>

View File

@@ -1,11 +1,11 @@
import React, { useEffect, useMemo, useState, useRef, lazy, Suspense, Component } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { usePublishSubplebbitEdit, useSubplebbit } from '@bitsocial/bitsocial-react-hooks';
import { usePublishCommunityEdit, useCommunity } from '@bitsocial/bitsocial-react-hooks';
import useTheme from '../../../stores/use-theme-store';
import styles from '../../settings/account-data-editor/account-data-editor.module.css';
import useIsMobile from '../../../hooks/use-is-mobile';
import LoadingEllipsis from '../../../components/loading-ellipsis';
import useSubplebbitSettingsStore from '../../../stores/use-subplebbit-settings-store';
import useCommunitySettingsStore from '../../../stores/use-community-settings-store';
import { useNavigate, useParams } from 'react-router-dom';
import ErrorDisplay from '../../../components/error-display';
import useStateString from '../../../hooks/use-state-string';
@@ -51,22 +51,22 @@ const FallbackEditor = ({ value, onChange, height, disabled }: { value: string;
);
};
const SubplebbitDataEditor = () => {
const CommunityDataEditor = () => {
const { t } = useTranslation();
const isMobile = useIsMobile();
const navigate = useNavigate();
const theme = useTheme((state) => state.theme);
const [text, setText] = useState('');
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
const subplebbit = useSubplebbit({ subplebbitAddress });
const { communityAddress: subplebbitAddress } = useParams<{ communityAddress: string }>();
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress } } : undefined);
const { address, createdAt, description, error, rules, settings, suggested, roles, title } = subplebbit || {};
const hasLoaded = !!createdAt;
const {
publishSubplebbitEditOptions,
setSubplebbitSettingsStore,
resetSubplebbitSettingsStore,
publishCommunityEditOptions,
setCommunitySettingsStore,
resetCommunitySettingsStore,
title: storeTitle,
description: storeDescription,
address: storeAddress,
@@ -74,14 +74,14 @@ const SubplebbitDataEditor = () => {
rules: storeRules,
roles: storeRoles,
settings: storeSettings,
subplebbitAddress: storeSubplebbitAddress,
} = useSubplebbitSettingsStore();
communityAddress: storeCommunityAddress,
} = useCommunitySettingsStore();
const { error: publishSubplebbitEditError, publishSubplebbitEdit } = usePublishSubplebbitEdit(publishSubplebbitEditOptions);
const { error: publishCommunityEditError, publishCommunityEdit: publishSubplebbitEdit } = usePublishCommunityEdit(publishCommunityEditOptions);
// Use store state if available, otherwise fall back to original subplebbit data
const currentSettings = useMemo(() => {
const { subplebbitAddress: storeAddr } = useSubplebbitSettingsStore.getState();
const { communityAddress: storeAddr } = useCommunitySettingsStore.getState();
const hasStoreData = storeAddr === subplebbitAddress;
return {
@@ -92,7 +92,7 @@ const SubplebbitDataEditor = () => {
rules: hasStoreData ? storeRules : rules,
roles: hasStoreData ? storeRoles : roles,
settings: hasStoreData ? storeSettings : settings,
subplebbitAddress: hasStoreData ? storeSubplebbitAddress : subplebbitAddress,
communityAddress: hasStoreData ? storeCommunityAddress : subplebbitAddress,
};
}, [
storeTitle,
@@ -102,7 +102,7 @@ const SubplebbitDataEditor = () => {
storeRules,
storeRoles,
storeSettings,
storeSubplebbitAddress,
storeCommunityAddress,
title,
description,
address,
@@ -142,7 +142,7 @@ const SubplebbitDataEditor = () => {
// Try to sync immediately if JSON is valid
try {
const parsedSettings = JSON.parse(newText);
setSubplebbitSettingsStore({
setCommunitySettingsStore({
title: parsedSettings.title ?? '',
description: parsedSettings.description ?? '',
address: parsedSettings.address,
@@ -150,7 +150,7 @@ const SubplebbitDataEditor = () => {
rules: parsedSettings.rules ?? [],
roles: parsedSettings.roles ?? {},
settings: parsedSettings.settings ?? {},
subplebbitAddress: parsedSettings.subplebbitAddress,
communityAddress: parsedSettings.communityAddress,
});
} catch {
// Invalid JSON - don't spam console during active typing
@@ -167,14 +167,14 @@ const SubplebbitDataEditor = () => {
if (triggerSave) {
const performSave = async () => {
try {
console.log('Performing save with options:', publishSubplebbitEditOptions);
console.log('Performing save with options:', publishCommunityEditOptions);
await publishSubplebbitEdit();
setShowSaving(false);
setTriggerSave(false);
if (publishSubplebbitEditError) {
setCurrentError(publishSubplebbitEditError);
alert(publishSubplebbitEditError.message || 'Error: ' + publishSubplebbitEditError);
if (publishCommunityEditError) {
setCurrentError(publishCommunityEditError);
alert(publishCommunityEditError.message || 'Error: ' + publishCommunityEditError);
} else {
alert(t('settings_saved', { subplebbitAddress }));
}
@@ -192,11 +192,11 @@ const SubplebbitDataEditor = () => {
};
performSave();
}
// Intentionally only depend on triggerSave to prevent multiple executions when publishSubplebbitEditOptions changes during save
// Intentionally only depend on triggerSave to prevent multiple executions when publishCommunityEditOptions changes during save
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [triggerSave]);
const saveSubplebbitSettings = () => {
const saveCommunitySettings = () => {
try {
setShowSaving(true);
setCurrentError(undefined);
@@ -231,12 +231,12 @@ const SubplebbitDataEditor = () => {
useEffect(() => {
if (hasLoaded) {
// Only reset if we're switching to a different subplebbit or if store is uninitialized
const { subplebbitAddress: storeSubplebbitAddress } = useSubplebbitSettingsStore.getState();
const shouldReset = !storeSubplebbitAddress || storeSubplebbitAddress !== subplebbitAddress;
const { communityAddress: storeCommunityAddress } = useCommunitySettingsStore.getState();
const shouldReset = !storeCommunityAddress || storeCommunityAddress !== subplebbitAddress;
if (shouldReset) {
resetSubplebbitSettingsStore();
setSubplebbitSettingsStore({
resetCommunitySettingsStore();
setCommunitySettingsStore({
title: title ?? '',
description: description ?? '',
address,
@@ -244,11 +244,11 @@ const SubplebbitDataEditor = () => {
rules: rules ?? [],
roles: roles ?? {},
settings: settings ?? {},
subplebbitAddress,
communityAddress: subplebbitAddress,
});
}
}
}, [hasLoaded, resetSubplebbitSettingsStore, setSubplebbitSettingsStore, title, description, address, suggested, rules, roles, settings, subplebbitAddress]);
}, [hasLoaded, resetCommunitySettingsStore, setCommunitySettingsStore, title, description, address, suggested, rules, roles, settings, subplebbitAddress]);
const loadingStateString = useStateString(subplebbit);
@@ -319,7 +319,7 @@ const SubplebbitDataEditor = () => {
<Trans
i18nKey='save_reset_changes'
components={{
1: <button key='saveSubplebbitSettingsButton' onClick={saveSubplebbitSettings} />,
1: <button key='saveCommunitySettingsButton' onClick={saveCommunitySettings} />,
2: <button key='resetSubplebbitSettingsButton' onClick={() => setText(subplebbitSettings)} />,
}}
/>
@@ -333,4 +333,4 @@ const SubplebbitDataEditor = () => {
);
};
export default SubplebbitDataEditor;
export default CommunityDataEditor;

View File

@@ -0,0 +1 @@
export { default } from './community-data-editor';

View File

@@ -2,20 +2,20 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
deleteSubplebbit,
deleteCommunity,
Role,
useAccount,
useCreateSubplebbit,
usePlebbitRpcSettings,
usePublishSubplebbitEdit,
useSubplebbit,
useCreateCommunity,
usePkcRpcSettings,
usePublishCommunityEdit,
useCommunity,
useSubscribe,
} from '@bitsocial/bitsocial-react-hooks';
import { isUserOwnerOrAdmin, Roles } from '../../lib/utils/user-utils';
import { isValidURL } from '../../lib/utils/url-utils';
import { isCreateSubplebbitView, isSubplebbitSettingsView } from '../../lib/utils/view-utils';
import useSubplebbitSettingsStore from '../../stores/use-subplebbit-settings-store';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
import { isCreateCommunityView, isCommunitySettingsView } from '../../lib/utils/view-utils';
import useCommunitySettingsStore from '../../stores/use-community-settings-store';
import useIsCommunityOffline from '../../hooks/use-is-community-offline';
import useStateString from '../../hooks/use-state-string';
import ErrorDisplay from '../../components/error-display';
import LoadingEllipsis from '../../components/loading-ellipsis';
@@ -23,19 +23,19 @@ import Markdown from '../../components/markdown';
import Sidebar from '../../components/sidebar';
import Challenges from './challenge-settings';
import { FormattingHelpTable } from '../../components/reply-form';
import styles from './subplebbit-settings.module.css';
import styles from './community-settings.module.css';
import _ from 'lodash';
const Title = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const { title, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { title, setCommunitySettingsStore } = useCommunitySettingsStore();
return (
<div className={`${styles.box} ${isReadOnly && !title ? styles.hidden : styles.visible}`}>
<div className={styles.boxTitle}>{t('title')}</div>
<div className={styles.boxSubtitle}>{t('a_short_title')}</div>
<div className={styles.boxInput}>
{isReadOnly ? <span>{title}</span> : <input type='text' value={title ?? ''} onChange={(e) => setSubplebbitSettingsStore({ title: e.target.value })} />}
{isReadOnly ? <span>{title}</span> : <input type='text' value={title ?? ''} onChange={(e) => setCommunitySettingsStore({ title: e.target.value })} />}
</div>
</div>
);
@@ -43,7 +43,7 @@ const Title = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const Description = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const { description, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { description, setCommunitySettingsStore } = useCommunitySettingsStore();
const [showFormattingHelp, setShowFormattingHelp] = useState(false);
const [showPreview, setShowPreview] = useState(false);
@@ -57,7 +57,7 @@ const Description = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
) : (
<>
{!showPreview ? (
<textarea value={description ?? ''} onChange={(e) => setSubplebbitSettingsStore({ description: e.target.value })} />
<textarea value={description ?? ''} onChange={(e) => setCommunitySettingsStore({ description: e.target.value })} />
) : (
<div className={styles.preview}>
<Markdown content={description ?? ''} />
@@ -96,7 +96,7 @@ const Description = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const Address = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const { address, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { address, setCommunitySettingsStore } = useCommunitySettingsStore();
const alertCryptoAddressInfo = () => {
alert(`steps to set a .eth community address:\n1. go to app.ens.domains and search the address\n2. once you own the address, go to its page, click on "records", then "edit records"\n3. add a new text record with name "subplebbit-address" and value: ${address}\n\n steps to set a .sol community address:\n1. go to v1.sns.id and search the address\n2. once you own the address, go to your profile, click the address menu "...", then "create subdomain"\n3. enter subdomain "subplebbit-address" and create\n4. go to subdomain, "content", change content to: ${address}
@@ -114,7 +114,7 @@ const Address = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
{isReadOnly ? (
<span className={styles.readOnlyAddress}>{address}</span>
) : (
<input type='text' value={address ?? ''} onChange={(e) => setSubplebbitSettingsStore({ address: e.target.value })} />
<input type='text' value={address ?? ''} onChange={(e) => setCommunitySettingsStore({ address: e.target.value })} />
)}
</div>
</div>
@@ -123,7 +123,7 @@ const Address = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const Logo = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const { suggested, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { suggested, setCommunitySettingsStore } = useCommunitySettingsStore();
const [logoUrl, setLogoUrl] = useState(suggested?.avatarUrl);
const [imageError, setImageError] = useState(false);
@@ -147,7 +147,7 @@ const Logo = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
onChange={(e) => {
setLogoUrl(e.target.value.trim());
setImageError(false);
setSubplebbitSettingsStore({ suggested: { ...suggested, avatarUrl: e.target.value.trim() || undefined } });
setCommunitySettingsStore({ suggested: { ...suggested, avatarUrl: e.target.value.trim() || undefined } });
}}
/>
)}
@@ -164,20 +164,20 @@ const Logo = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const Rules = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const { rules, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { rules, setCommunitySettingsStore } = useCommunitySettingsStore();
const lastRuleRef = useRef(null);
const handleRuleChange = (index: number, newRule: string) => {
if (!rules) return;
const updatedRules = [...(rules ?? [])];
updatedRules[index] = newRule;
setSubplebbitSettingsStore({ rules: updatedRules });
setCommunitySettingsStore({ rules: updatedRules });
};
const addedRuleRef = useRef(false);
const addRule = () => {
const newRules = rules ? [...rules, ''] : [''];
setSubplebbitSettingsStore({ rules: newRules });
setCommunitySettingsStore({ rules: newRules });
addedRuleRef.current = true;
};
@@ -191,7 +191,7 @@ const Rules = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const deleteRule = (index: number) => {
if (rules) {
const filteredRules = rules.filter((_, i) => i !== index);
setSubplebbitSettingsStore({ rules: filteredRules });
setCommunitySettingsStore({ rules: filteredRules });
}
};
@@ -224,17 +224,17 @@ const Rules = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const Moderators = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const { roles, setSubplebbitSettingsStore } = useSubplebbitSettingsStore();
const { roles, setCommunitySettingsStore } = useCommunitySettingsStore();
const lastModeratorRef = useRef(null);
const addedModeratorRef = useRef(false);
const handleAddModerator = () => {
if (roles) {
const newRoles: Roles = { ...roles, '': { role: 'moderator' } };
setSubplebbitSettingsStore({ roles: newRoles });
setCommunitySettingsStore({ roles: newRoles });
addedModeratorRef.current = true;
} else {
setSubplebbitSettingsStore({ roles: { '': { role: 'moderator' } } });
setCommunitySettingsStore({ roles: { '': { role: 'moderator' } } });
addedModeratorRef.current = true;
}
};
@@ -250,14 +250,14 @@ const Moderators = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
if (roles) {
const updatedRole: Role = { role: newRole };
const updatedRoles: Roles = { ...roles, [address]: updatedRole };
setSubplebbitSettingsStore({ roles: updatedRoles });
setCommunitySettingsStore({ roles: updatedRoles });
}
};
const handleDeleteModerator = (address: string) => {
if (roles) {
const { [address]: _, ...remainingRoles } = roles;
setSubplebbitSettingsStore({ roles: remainingRoles });
setCommunitySettingsStore({ roles: remainingRoles });
}
};
@@ -265,7 +265,7 @@ const Moderators = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const rolesArray = Object.entries(roles || {});
rolesArray[index] = [newAddress, rolesArray[index][1]];
const updatedRoles = Object.fromEntries(rolesArray);
setSubplebbitSettingsStore({ roles: updatedRoles });
setCommunitySettingsStore({ roles: updatedRoles });
};
return (
@@ -325,7 +325,7 @@ const Moderators = ({ isReadOnly = false }: { isReadOnly?: boolean }) => {
const JSONSettings = ({ isReadOnly: _isReadOnly = false }: { isReadOnly?: boolean }) => {
const { t } = useTranslation();
const navigate = useNavigate();
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
const { communityAddress: subplebbitAddress } = useParams<{ communityAddress: string }>();
return (
<div className={`${styles.box}`}>
@@ -338,63 +338,63 @@ const JSONSettings = ({ isReadOnly: _isReadOnly = false }: { isReadOnly?: boolea
);
};
const SubplebbitSettings = () => {
const CommunitySettings = () => {
const { t } = useTranslation();
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
const subplebbit = useSubplebbit({ subplebbitAddress });
const { communityAddress: subplebbitAddress } = useParams<{ communityAddress: string }>();
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress } } : undefined);
const { address, challenges, createdAt, description, error, rules, shortAddress, settings, suggested, roles, title } = subplebbit || {};
const hasLoaded = !!createdAt;
const { challenges: rpcChallenges } = usePlebbitRpcSettings().plebbitRpcSettings || {};
const { challenges: rpcChallenges } = usePkcRpcSettings().pkcRpcSettings || {};
const challengeNames = Object.keys(rpcChallenges || {});
const account = useAccount();
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
const isInCreateSubplebbitView = isCreateSubplebbitView(location.pathname);
const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params);
const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected';
const isInCreateCommunityView = isCreateCommunityView(location.pathname);
const isInCommunitySettingsView = isCommunitySettingsView(location.pathname, params);
const isConnectedToRpc = usePkcRpcSettings()?.state === 'connected';
if (isInCreateSubplebbitView && !isConnectedToRpc) {
if (isInCreateCommunityView && !isConnectedToRpc) {
navigate('/', { replace: true });
}
const userAddress = account?.author?.address;
const userIsOwnerOrAdmin = isUserOwnerOrAdmin(roles, userAddress);
const { isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit || {});
const { isOffline, offlineTitle } = useIsCommunityOffline(subplebbit || {});
// General fields can be edited by owners/admins even without RPC connection
const isReadOnly = (!settings && isInSubplebbitSettingsView && !userIsOwnerOrAdmin) || (!isConnectedToRpc && isInCreateSubplebbitView && !userIsOwnerOrAdmin);
const isReadOnly = (!settings && isInCommunitySettingsView && !userIsOwnerOrAdmin) || (!isConnectedToRpc && isInCreateCommunityView && !userIsOwnerOrAdmin);
// Challenges are always read-only when not connected to RPC
const isChallengesReadOnly = (!isConnectedToRpc || !settings) && !isInCreateSubplebbitView;
const isChallengesReadOnly = (!isConnectedToRpc || !settings) && !isInCreateCommunityView;
const { publishSubplebbitEditOptions, resetSubplebbitSettingsStore, setSubplebbitSettingsStore, title: storeTitle } = useSubplebbitSettingsStore();
const { error: publishSubplebbitEditError, publishSubplebbitEdit } = usePublishSubplebbitEdit(publishSubplebbitEditOptions);
const { error: createSubplebbitError, createdSubplebbit, createSubplebbit } = useCreateSubplebbit(publishSubplebbitEditOptions);
const { publishCommunityEditOptions, resetCommunitySettingsStore, setCommunitySettingsStore, title: storeTitle } = useCommunitySettingsStore();
const { error: publishCommunityEditError, publishCommunityEdit: publishSubplebbitEdit } = usePublishCommunityEdit(publishCommunityEditOptions);
const { error: createCommunityError, createdCommunity, createCommunity: createSubplebbit } = useCreateCommunity(publishCommunityEditOptions);
const [showSaving, setShowSaving] = useState(false);
const [currentError, setCurrentError] = useState<Error | undefined>(undefined);
useEffect(() => {
if (publishSubplebbitEditError || createSubplebbitError) {
setCurrentError(publishSubplebbitEditError || createSubplebbitError);
if (publishCommunityEditError || createCommunityError) {
setCurrentError(publishCommunityEditError || createCommunityError);
}
}, [publishSubplebbitEditError, createSubplebbitError]);
}, [publishCommunityEditError, createCommunityError]);
const saveSubplebbit = async () => {
try {
setShowSaving(true);
setCurrentError(undefined);
console.log('Saving subplebbit with options:', publishSubplebbitEditOptions);
console.log('Saving subplebbit with options:', publishCommunityEditOptions);
await publishSubplebbitEdit();
setShowSaving(false);
if (publishSubplebbitEditError) {
setCurrentError(publishSubplebbitEditError);
alert(publishSubplebbitEditError.message || 'Error: ' + publishSubplebbitEditError);
if (publishCommunityEditError) {
setCurrentError(publishCommunityEditError);
alert(publishCommunityEditError.message || 'Error: ' + publishCommunityEditError);
} else {
alert(t('settings_saved', { subplebbitAddress }));
}
@@ -411,12 +411,12 @@ const SubplebbitSettings = () => {
};
const [showDeleting, setShowDeleting] = useState(false);
const _deleteSubplebbit = async () => {
const _deleteCommunity = async () => {
if (subplebbitAddress && window.confirm(t('delete_confirm', { value: `s/${shortAddress}`, interpolation: { escapeValue: false } }))) {
if (window.confirm(t('double_confirm'))) {
try {
setShowDeleting(true);
await deleteSubplebbit(subplebbitAddress);
await deleteCommunity(subplebbitAddress);
setShowDeleting(false);
alert(t('community_deleted'));
navigate('/', { replace: true });
@@ -432,17 +432,17 @@ const SubplebbitSettings = () => {
}
};
const _createSubplebbit = async () => {
const _createCommunity = async () => {
try {
setShowSaving(true);
setCurrentError(undefined);
console.log('Creating subplebbit with settings:', publishSubplebbitEditOptions);
console.log('Creating subplebbit with settings:', publishCommunityEditOptions);
await createSubplebbit();
setShowSaving(false);
if (createSubplebbitError) {
setCurrentError(createSubplebbitError);
alert(createSubplebbitError.message || 'Error: ' + createSubplebbitError);
if (createCommunityError) {
setCurrentError(createCommunityError);
alert(createCommunityError.message || 'Error: ' + createCommunityError);
}
} catch (e) {
setShowSaving(false);
@@ -456,30 +456,30 @@ const SubplebbitSettings = () => {
}
};
const { subscribe } = useSubscribe({ subplebbitAddress: createdSubplebbit?.address });
const { subscribe } = useSubscribe({ communityAddress: createdCommunity?.address });
useEffect(() => {
if (createdSubplebbit) {
console.log('createdSubplebbit', createdSubplebbit);
alert(`community created, address: ${createdSubplebbit?.address}`);
if (createdCommunity) {
console.log('createdCommunity', createdCommunity);
alert(`community created, address: ${createdCommunity?.address}`);
if (account && createdSubplebbit.address) {
if (account && createdCommunity.address) {
subscribe();
}
resetSubplebbitSettingsStore();
navigate(`/s/${createdSubplebbit?.address}/`);
resetCommunitySettingsStore();
navigate(`/s/${createdCommunity?.address}/`);
}
}, [createdSubplebbit, navigate, resetSubplebbitSettingsStore, account, subscribe]);
}, [createdCommunity, navigate, resetCommunitySettingsStore, account, subscribe]);
const lastViewType = useRef<'create' | 'settings' | 'other' | undefined>(undefined);
// Initialize store for create view only on first entry or when switching from settings view
useEffect(() => {
if (isInCreateSubplebbitView && lastViewType.current === 'settings') {
resetSubplebbitSettingsStore();
if (isInCreateCommunityView && lastViewType.current === 'settings') {
resetCommunitySettingsStore();
const initialRoles: Roles = account?.author?.address ? { [account.author.address]: { role: 'owner' as const } } : {};
setSubplebbitSettingsStore({
setCommunitySettingsStore({
title: '',
description: '',
address: undefined,
@@ -488,28 +488,28 @@ const SubplebbitSettings = () => {
roles: initialRoles,
settings: {},
challenges: [],
subplebbitAddress: undefined,
communityAddress: undefined,
});
}
if (isInCreateSubplebbitView) {
if (isInCreateCommunityView) {
lastViewType.current = 'create';
} else if (isInSubplebbitSettingsView) {
} else if (isInCommunitySettingsView) {
lastViewType.current = 'settings';
} else {
lastViewType.current = 'other';
}
}, [isInCreateSubplebbitView, storeTitle, resetSubplebbitSettingsStore, setSubplebbitSettingsStore, account, isInSubplebbitSettingsView]);
}, [isInCreateCommunityView, storeTitle, resetCommunitySettingsStore, setCommunitySettingsStore, account, isInCommunitySettingsView]);
// Set store for loaded subplebbit settings when editing
useEffect(() => {
if (!isInCreateSubplebbitView && hasLoaded) {
if (!isInCreateCommunityView && hasLoaded) {
// Only reset if we're switching to a different subplebbit or if store is uninitialized
const { subplebbitAddress: storeSubplebbitAddress } = useSubplebbitSettingsStore.getState();
const shouldReset = !storeSubplebbitAddress || storeSubplebbitAddress !== subplebbitAddress;
const { communityAddress: storeCommunityAddress } = useCommunitySettingsStore.getState();
const shouldReset = !storeCommunityAddress || storeCommunityAddress !== subplebbitAddress;
if (shouldReset) {
resetSubplebbitSettingsStore();
setSubplebbitSettingsStore({
resetCommunitySettingsStore();
setCommunitySettingsStore({
title: title ?? '',
description: description ?? '',
address,
@@ -518,15 +518,15 @@ const SubplebbitSettings = () => {
roles: roles ?? {},
settings: settings ?? {},
challenges: challenges ?? [],
subplebbitAddress,
communityAddress: subplebbitAddress,
});
}
}
}, [
isInCreateSubplebbitView,
isInCreateCommunityView,
hasLoaded,
resetSubplebbitSettingsStore,
setSubplebbitSettingsStore,
resetCommunitySettingsStore,
setCommunitySettingsStore,
title,
description,
address,
@@ -540,13 +540,13 @@ const SubplebbitSettings = () => {
const documentTitle = useMemo(() => {
let title;
if (isInSubplebbitSettingsView) {
if (isInCommunitySettingsView) {
title = _.startCase(t('community_settings', { interpolation: { escapeValue: false } }));
} else if (isInCreateSubplebbitView) {
} else if (isInCreateCommunityView) {
title = _.startCase(t('create_community', { interpolation: { escapeValue: false } }));
}
return `${title} - Seedit`;
}, [isInCreateSubplebbitView, isInSubplebbitSettingsView, t]);
}, [isInCreateCommunityView, isInCommunitySettingsView, t]);
useEffect(() => {
document.title = documentTitle;
@@ -558,7 +558,7 @@ const SubplebbitSettings = () => {
const loadingStateString = useStateString(subplebbit);
if (!hasLoaded && !isInCreateSubplebbitView) {
if (!hasLoaded && !isInCreateCommunityView) {
return (
<>
{error?.message && (
@@ -575,7 +575,7 @@ const SubplebbitSettings = () => {
return (
<div className={styles.content}>
{!isInCreateSubplebbitView && (
{!isInCreateCommunityView && (
<div className={styles.sidebar}>
<Sidebar subplebbit={subplebbit} />
</div>
@@ -585,20 +585,20 @@ const SubplebbitSettings = () => {
{isChallengesReadOnly && <div className={styles.infobar}>cannot read or write challenges, community node isn't reachable.</div>}
<Title isReadOnly={isReadOnly} />
<Description isReadOnly={isReadOnly} />
{!isInCreateSubplebbitView && <Address isReadOnly={isReadOnly} />}
{!isInCreateCommunityView && <Address isReadOnly={isReadOnly} />}
<Logo isReadOnly={isReadOnly} />
<Rules isReadOnly={isReadOnly} />
<Moderators isReadOnly={isReadOnly} />
<Challenges isReadOnly={isChallengesReadOnly} readOnlyChallenges={subplebbit?.challenges} challengeNames={challengeNames} challengesSettings={rpcChallenges} />
{!isInCreateSubplebbitView && <JSONSettings isReadOnly={isReadOnly} />}
{!isInCreateCommunityView && <JSONSettings isReadOnly={isReadOnly} />}
<div className={styles.saveOptions}>
{!isInCreateSubplebbitView && !isReadOnly && (
{!isInCreateCommunityView && !isReadOnly && (
<div className={`${styles.box} ${styles.deleteCommunity}`}>
<div className={styles.boxTitle}>{t('delete_community')}</div>
<div className={styles.boxSubtitle}>{t('delete_community_description')}</div>
<div className={styles.boxInput}>
<div className={styles.deleteSubplebbit}>
<button onClick={_deleteSubplebbit} disabled={showDeleting || showSaving}>
<button onClick={_deleteCommunity} disabled={showDeleting || showSaving}>
{t('delete')}
</button>
<span className={styles.deletingString}>{showDeleting && <LoadingEllipsis string={t('deleting')} />}</span>
@@ -607,8 +607,8 @@ const SubplebbitSettings = () => {
</div>
)}
{!isReadOnly && (
<button onClick={() => (isInCreateSubplebbitView ? _createSubplebbit() : saveSubplebbit())} disabled={showSaving || showDeleting}>
{isInCreateSubplebbitView ? t('create_community') : t('save_options')}
<button onClick={() => (isInCreateCommunityView ? _createCommunity() : saveSubplebbit())} disabled={showSaving || showDeleting}>
{isInCreateCommunityView ? t('create_community') : t('save_options')}
</button>
)}
{showSaving && <LoadingEllipsis string={t('saving')} />}
@@ -622,4 +622,4 @@ const SubplebbitSettings = () => {
);
};
export default SubplebbitSettings;
export default CommunitySettings;

View File

@@ -0,0 +1 @@
export { default } from './community-settings';

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { Link, useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { useAccountComments, useBlock, useFeed, useSubplebbit, type Comment } from '@bitsocial/bitsocial-react-hooks';
import { useAccountComments, useBlock, useFeed, useCommunity, type Comment } from '@bitsocial/bitsocial-react-hooks';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { Trans, useTranslation } from 'react-i18next';
import styles from '../home/home.module.css';
@@ -12,8 +12,9 @@ import useFeedFiltersStore from '../../stores/use-feed-filters-store';
import useFeedResetStore from '../../stores/use-feed-reset-store';
import { usePinnedPostsStore } from '../../stores/use-pinned-posts-store';
import { useIsBroadlyNsfwSubplebbit } from '../../hooks/use-is-broadly-nsfw-subplebbit';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
import useIsCommunityOffline from '../../hooks/use-is-community-offline';
import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter';
import { getCommunityIdentifiers } from '../../hooks/use-community-identifier';
import ErrorDisplay from '../../components/error-display';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Over18Warning from '../../components/over-18-warning';
@@ -213,16 +214,16 @@ const Footer = ({
);
};
const Subplebbit = () => {
const CommunityView = () => {
const params = useParams();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const searchQuery = searchParams.get('q') || '';
const subplebbitAddress = params?.subplebbitAddress || '';
const subplebbit = useSubplebbit({ subplebbitAddress });
const subplebbitAddress = params?.communityAddress || '';
const subplebbit = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress } } : undefined);
const { createdAt, error, shortAddress, started, title, updatedAt, settings } = subplebbit || {};
const { isOffline } = useIsSubplebbitOffline(subplebbit || {});
const { isOffline } = useIsCommunityOffline(subplebbit || {});
const isOnline = !isOffline;
const isSubCreatedButNotYetPublished = typeof createdAt === 'number' && !updatedAt;
@@ -248,7 +249,7 @@ const Subplebbit = () => {
const feedOptions = useMemo(() => {
const options: any = {
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: searchQuery ? 0 : timeFilterSeconds,
};
@@ -392,4 +393,4 @@ const Subplebbit = () => {
);
};
export default Subplebbit;
export default CommunityView;

View File

@@ -0,0 +1 @@
export { default } from './community';

View File

@@ -7,6 +7,7 @@ import useFeedFiltersStore from '../../stores/use-feed-filters-store';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter';
import FeedFooter from '../../components/feed-footer';
import { getCommunityIdentifiers } from '../../hooks/use-community-identifier';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Post from '../../components/post';
import Sidebar from '../../components/sidebar';
@@ -67,19 +68,19 @@ const Domain = () => {
const options: {
newerThan: number;
sortType: string;
subplebbitAddresses: string[];
communities: ReturnType<typeof getCommunityIdentifiers>;
filter: CommentsFilter;
} = {
newerThan: searchQuery ? 0 : (timeFilterSeconds ?? 0),
sortType,
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
filter: { filter: filterFunc, key: filterKey },
};
return options;
}, [subplebbitAddresses, sortType, timeFilterSeconds, searchQuery, matchesDomain, domain]);
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
const { feed, hasMore, loadMore, reset, communityKeysWithNewerPosts: subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
useEffect(() => {
if (isSearching) {
@@ -108,21 +109,21 @@ const Domain = () => {
// suggest the user to change time filter if there aren't enough posts
const { feed: weeklyFeed } = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 7,
filter: { filter: matchesDomain, key: `domain-filter-weekly-${domain}` },
});
const { feed: monthlyFeed } = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 30,
filter: { filter: matchesDomain, key: `domain-filter-monthly-${domain}` },
});
const { feed: yearlyFeed } = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 365,
filter: { filter: matchesDomain, key: `domain-filter-yearly-${domain}` },

View File

@@ -8,6 +8,7 @@ import useFeedFiltersStore from '../../stores/use-feed-filters-store';
import { useAutoSubscribeStore } from '../../stores/use-auto-subscribe-store';
import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter';
import useRedirectToDefaultSort from '../../hooks/use-redirect-to-default-sort';
import { getCommunityIdentifiers } from '../../hooks/use-community-identifier';
import FeedFooter from '../../components/feed-footer';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Post from '../../components/post';
@@ -75,7 +76,7 @@ const Home = () => {
newerThan: searchQuery ? 0 : timeFilterSeconds,
postsPerPage: 10,
sortType,
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
};
if (searchQuery) {
@@ -88,7 +89,7 @@ const Home = () => {
return options;
}, [subplebbitAddresses, sortType, timeFilterSeconds, searchQuery, commentFilter]);
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
const { feed, hasMore, loadMore, reset, communityKeysWithNewerPosts: subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
useEffect(() => {
startTransition(() => {
@@ -126,7 +127,7 @@ const Home = () => {
hasMore: hasMoreWeekly,
loadMore: loadMoreWeekly,
} = useFeed({
subplebbitAddresses: shouldLoadAdditionalFeeds ? subplebbitAddresses : [],
communities: getCommunityIdentifiers(shouldLoadAdditionalFeeds ? subplebbitAddresses : []),
sortType,
newerThan: 60 * 60 * 24 * 7,
});
@@ -135,7 +136,7 @@ const Home = () => {
hasMore: hasMoreMonthly,
loadMore: loadMoreMonthly,
} = useFeed({
subplebbitAddresses: shouldLoadAdditionalFeeds ? subplebbitAddresses : [],
communities: getCommunityIdentifiers(shouldLoadAdditionalFeeds ? subplebbitAddresses : []),
sortType,
newerThan: 60 * 60 * 24 * 30,
});
@@ -144,7 +145,7 @@ const Home = () => {
hasMore: hasMoreYearly,
loadMore: loadMoreYearly,
} = useFeed({
subplebbitAddresses: shouldLoadAdditionalFeeds ? subplebbitAddresses : [],
communities: getCommunityIdentifiers(shouldLoadAdditionalFeeds ? subplebbitAddresses : []),
sortType,
newerThan: 60 * 60 * 24 * 365,
});

View File

@@ -1,12 +1,13 @@
import { useEffect, useRef, useState, useMemo } from 'react';
import { useParams, useSearchParams, useNavigate, useLocation } from 'react-router-dom';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { useAccountSubplebbits, useFeed } from '@bitsocial/bitsocial-react-hooks';
import { useAccountCommunities, useFeed } from '@bitsocial/bitsocial-react-hooks';
import { useTranslation } from 'react-i18next';
import { commentMatchesPattern } from '../../lib/utils/pattern-utils';
import useFeedFiltersStore from '../../stores/use-feed-filters-store';
import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter';
import FeedFooter from '../../components/feed-footer';
import { getCommunityIdentifiers } from '../../hooks/use-community-identifier';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Post from '../../components/post';
import Sidebar from '../../components/sidebar';
@@ -16,7 +17,7 @@ import styles from '../home/home.module.css';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
const Mod = () => {
const { accountSubplebbits } = useAccountSubplebbits();
const { accountCommunities: accountSubplebbits } = useAccountCommunities();
const subplebbitAddresses = Object.keys(accountSubplebbits);
const params = useParams<{ sortType?: string; timeFilterName?: string }>();
const [searchParams, setSearchParams] = useSearchParams();
@@ -45,7 +46,7 @@ const Mod = () => {
const options: any = {
newerThan: searchQuery ? 0 : timeFilterSeconds,
sortType,
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
};
if (searchQuery) {
@@ -61,7 +62,7 @@ const Mod = () => {
return options;
}, [subplebbitAddresses, sortType, timeFilterSeconds, searchQuery]);
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
const { feed, hasMore, loadMore, reset, communityKeysWithNewerPosts: subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
// suggest the user to change time filter if there aren't enough posts
const {
@@ -69,7 +70,7 @@ const Mod = () => {
hasMore: hasMoreWeekly,
loadMore: loadMoreWeekly,
} = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 7,
});
@@ -78,7 +79,7 @@ const Mod = () => {
hasMore: hasMoreMonthly,
loadMore: loadMoreMonthly,
} = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 30,
});
@@ -87,7 +88,7 @@ const Mod = () => {
hasMore: hasMoreYearly,
loadMore: loadMoreYearly,
} = useFeed({
subplebbitAddresses,
communities: getCommunityIdentifiers(subplebbitAddresses),
sortType,
newerThan: 60 * 60 * 24 * 365,
});

View File

@@ -1,7 +1,7 @@
import { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Comment, useAccount, useAccountComment, useAccountComments, useComment, useSubplebbit } from '@bitsocial/bitsocial-react-hooks';
import { Comment, useAccount, useAccountComment, useAccountComments, useComment, useCommunity } from '@bitsocial/bitsocial-react-hooks';
import findTopParentCidOfReply from '../../lib/utils/cid-utils';
import { sortRepliesByBest } from '../../lib/utils/post-utils';
import { isPendingPostView, isPostContextView } from '../../lib/utils/view-utils';
@@ -274,12 +274,13 @@ const PostPage = () => {
}
}, [pendingPost?.cid, pendingPost?.subplebbitAddress, navigate, resetFeed]);
const { commentCid, subplebbitAddress } = params;
let post = useComment({ commentCid });
const { commentCid, communityAddress: subplebbitAddress } = params;
let post = useComment({ commentCid }) as any;
if (isInPendingPostView) {
post = pendingPost;
}
const subplebbit = useSubplebbit({ subplebbitAddress: isInPendingPostView ? pendingPost?.subplebbitAddress : subplebbitAddress });
const _communityAddr = isInPendingPostView ? pendingPost?.subplebbitAddress : subplebbitAddress;
const subplebbit = useCommunity(_communityAddr ? { community: { name: _communityAddr } } : undefined);
// over 18 warning for subplebbit with nsfw tag in multisub default list
const { hasAcceptedWarning } = useContentOptionsStore();

View File

@@ -1,7 +1,7 @@
import { RefObject, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { setAccount, useAccount, usePlebbitRpcSettings } from '@bitsocial/bitsocial-react-hooks';
import { setAccount, useAccount, usePkcRpcSettings } from '@bitsocial/bitsocial-react-hooks';
import styles from './plebbit-options.module.css';
interface SettingsProps {
@@ -159,10 +159,10 @@ const PlebbitRPCSettings = ({ plebbitRpcRef }: SettingsProps) => {
};
const PlebbitDataPathSettings = ({ plebbitDataPathRef }: SettingsProps) => {
const plebbitRpc = usePlebbitRpcSettings();
const { plebbitRpcSettings } = plebbitRpc || {};
const plebbitRpc = usePkcRpcSettings();
const { pkcRpcSettings } = plebbitRpc || {};
const isConnectedToRpc = plebbitRpc?.state === 'connected';
const path = plebbitRpcSettings?.plebbitOptions?.dataPath || '';
const path = pkcRpcSettings?.plebbitOptions?.dataPath || '';
return (
<div className={styles.plebbitDataPathSettings}>

View File

@@ -2,15 +2,15 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { Trans, useTranslation } from 'react-i18next';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { useAccount, usePublishComment, useSubplebbit } from '@bitsocial/bitsocial-react-hooks';
import Plebbit from '@plebbit/plebbit-js';
import { useAccount, usePublishComment, useCommunity } from '@bitsocial/bitsocial-react-hooks';
import { Capacitor } from '@capacitor/core';
import FileUploader from '../../plugins/file-uploader';
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
import getShortAddress from '../../lib/utils/address-utils';
import { isValidURL } from '../../lib/utils/url-utils';
import usePublishPostStore from '../../stores/use-publish-post-store';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
import useIsCommunityOffline from '../../hooks/use-is-community-offline';
import LoadingEllipsis from '../../components/loading-ellipsis';
import Markdown from '../../components/markdown';
import Embed from '../../components/post/embed';
@@ -405,7 +405,7 @@ const SubplebbitAddressField = () => {
setPublishPostStore({ subplebbitAddress: subscription });
}}
>
{Plebbit.getShortAddress({ address: subscription })}
{getShortAddress(subscription)}
</span>
))}
</div>
@@ -470,13 +470,13 @@ const SubmitPage = () => {
const { link, title, subplebbitAddress, publishCommentOptions, setPublishPostStore, resetPublishPostStore } = usePublishPostStore();
useEffect(() => {
setPublishPostStore({ subplebbitAddress: params.subplebbitAddress || '' });
}, [params.subplebbitAddress, setPublishPostStore]);
setPublishPostStore({ subplebbitAddress: params.communityAddress || '' });
}, [params.communityAddress, setPublishPostStore]);
const selectedSubplebbitData = useSubplebbit({ subplebbitAddress });
const selectedSubplebbitData = useCommunity(subplebbitAddress ? { community: { name: subplebbitAddress } } : undefined);
const { rules, title: subplebbitTitle } = selectedSubplebbitData;
const shortAddress = subplebbitAddress && Plebbit.getShortAddress({ address: subplebbitAddress });
const { isOffline, offlineTitle } = useIsSubplebbitOffline(selectedSubplebbitData);
const shortAddress = subplebbitAddress && getShortAddress(subplebbitAddress);
const { isOffline, offlineTitle } = useIsCommunityOffline(selectedSubplebbitData);
const { index, publishComment } = usePublishComment(publishCommentOptions);

View File

@@ -1 +0,0 @@
export { default } from './subplebbit-settings';

View File

@@ -1 +0,0 @@
export { default } from './subplebbit-data-editor';

View File

@@ -1 +0,0 @@
export { default } from './subplebbit';

View File

@@ -1 +0,0 @@
export { default } from './subplebbits';