feat: auto subscribe new accounts to specific default subplebbits

This commit is contained in:
Tom (plebeius.eth)
2025-02-20 15:15:19 +01:00
parent 20726044e9
commit 882703b00d
10 changed files with 147 additions and 58 deletions
+19 -35
View File
@@ -5,7 +5,7 @@ import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { isAllView, isCatalogView, isSubscriptionsView } from '../../lib/utils/view-utils';
import styles from './topbar.module.css';
import useDefaultSubplebbits, { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import _, { debounce } from 'lodash';
import { TimeFilter } from '../board-buttons';
@@ -67,52 +67,36 @@ const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) =>
);
};
const renderBoardsList = (subplebbits: any, isInCatalogView: boolean, subscriptions: string[]) =>
subplebbits?.length > 0 && (
<>
[
{subplebbits.map((sub: any, index: any) => (
<span key={index}>
{index === 0 ? null : ' '}
<Link to={`/p/${sub.address}${isInCatalogView ? '/catalog' : ''}`}>
{sub.address.endsWith('.eth') || sub.address.endsWith('.sol') ? sub.address : sub.address.slice(0, 10).concat('...')}
</Link>
{index !== subplebbits.length - 1 ? ' /' : null}
</span>
))}
]{' '}
{subscriptions?.length > 0 && (
<>
[
{subscriptions.map((address: any, index: any) => (
<span key={index}>
{index === 0 ? null : ' '}
<Link to={`/p/${address}${isInCatalogView ? '/catalog' : ''}`}>
{address.endsWith('.eth') || address.endsWith('.sol') ? address : address.slice(0, 10).concat('...')}
</Link>
{index !== subscriptions?.length - 1 ? ' /' : null}
</span>
))}
]{' '}
</>
)}
</>
);
const TopBarDesktop = () => {
const { t } = useTranslation();
const account = useAccount();
const location = useLocation();
const params = useParams();
const isInCatalogView = isCatalogView(location.pathname, params);
const subplebbits = useDefaultSubplebbits();
const [showSearchBar, setShowSearchBar] = useState(false);
const subscriptions = account?.subscriptions;
return (
<div className={styles.boardNavDesktop}>
<span className={styles.boardList}>
[<Link to='/p/all'>all</Link> / <Link to='/p/subscriptions'>subscriptions</Link>]{' '}
{renderBoardsList(subplebbits.slice(0, 15), isInCatalogView, account?.subscriptions)}[
{subscriptions?.length > 0 && (
<>
[
{subscriptions.map((address: any, index: any) => (
<span key={index}>
{index === 0 ? null : ' '}
<Link to={`/p/${address}${isInCatalogView ? '/catalog' : ''}`}>
{address.endsWith('.eth') || address.endsWith('.sol') ? address : address.slice(0, 10).concat('...')}
</Link>
{index !== subscriptions?.length - 1 ? ' /' : null}
</span>
))}
]{' '}
</>
)}
[
<Link
className={styles.disabledButton}
to='boards/create'
+64
View File
@@ -0,0 +1,64 @@
import { useEffect } from 'react';
import { useAccount, setAccount } from '@plebbit/plebbit-react-hooks';
import { useAutoSubscribeStore } from '../stores/use-auto-subscribe-store';
import { getAutoSubscribeAddresses, useDefaultSubplebbits } from './use-default-subplebbits';
const AUTO_SUBSCRIBE_KEY_PREFIX = 'seedit-auto-subscribe-done-';
// Keep track of which accounts have been processed globally
const processedAccounts = new Set<string>();
export const useAutoSubscribe = () => {
const account = useAccount();
const accountAddress = account?.author?.address;
const defaultSubplebbits = useDefaultSubplebbits();
const { addCheckingAccount, removeCheckingAccount, isCheckingAccount } = useAutoSubscribeStore();
useEffect(() => {
if (!accountAddress) return;
// Mark as checking immediately when account changes
addCheckingAccount(accountAddress);
const processAutoSubscribe = async () => {
if (!account || !defaultSubplebbits?.length) return;
if (processedAccounts.has(accountAddress)) {
removeCheckingAccount(accountAddress);
return;
}
const storageKey = AUTO_SUBSCRIBE_KEY_PREFIX + accountAddress;
const hasAutoSubscribed = localStorage.getItem(storageKey);
if (account.subscriptions?.length > 0 || hasAutoSubscribed) {
processedAccounts.add(accountAddress);
removeCheckingAccount(accountAddress);
return;
}
const autoSubscribeAddresses = getAutoSubscribeAddresses();
if (autoSubscribeAddresses.length) {
try {
await setAccount({
...account,
subscriptions: autoSubscribeAddresses,
});
localStorage.setItem(storageKey, 'true');
processedAccounts.add(accountAddress);
} catch (error) {
console.error('Auto-subscribe error:', error);
}
removeCheckingAccount(accountAddress);
}
};
processAutoSubscribe();
return () => {
if (accountAddress) removeCheckingAccount(accountAddress);
};
}, [account, accountAddress, defaultSubplebbits, addCheckingAccount, removeCheckingAccount]);
return { isCheckingSubscriptions: accountAddress ? isCheckingAccount(accountAddress) : true };
};
+10 -2
View File
@@ -12,12 +12,14 @@ export interface MultisubSubplebbit {
address: string;
tags?: string[];
features?: string[];
plebchanAutoSubscribe?: boolean;
}
let cacheSubplebbits: MultisubSubplebbit[] | null = null;
let cacheMetadata: MultisubMetadata | null = null;
let cacheAutoSubscribeAddresses: string[] | null = null;
const useDefaultSubplebbits = () => {
export const useDefaultSubplebbits = () => {
const [subplebbits, setSubplebbits] = useState<MultisubSubplebbit[]>([]);
useEffect(() => {
@@ -31,6 +33,12 @@ const useDefaultSubplebbits = () => {
// { cache: 'no-cache' }
).then((res) => res.json());
cacheSubplebbits = multisub.subplebbits;
// Cache auto-subscribe addresses when we fetch subplebbits
cacheAutoSubscribeAddresses = multisub.subplebbits
.filter((sub: MultisubSubplebbit) => sub.plebchanAutoSubscribe && sub.address)
.map((sub: MultisubSubplebbit) => sub.address);
setSubplebbits(multisub.subplebbits);
} catch (e) {
console.warn(e);
@@ -72,4 +80,4 @@ export const useMultisubMetadata = () => {
return cacheMetadata || metadata;
};
export default useDefaultSubplebbits;
export const getAutoSubscribeAddresses = () => cacheAutoSubscribeAddresses || [];
+1 -1
View File
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import useThemeStore from '../stores/use-theme-store';
import useDefaultSubplebbits from './use-default-subplebbits';
import { useDefaultSubplebbits } from './use-default-subplebbits';
import { isAllView, isHomeView, isNotFoundView, isPendingPostView, isSubscriptionsView } from '../lib/utils/view-utils';
import { nsfwTags } from '../views/home/home';
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
+3 -3
View File
@@ -26,11 +26,11 @@ const useIsSubplebbitOffline = (subplebbit: Subplebbit) => {
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 isLoading = subplebbitOfflineStore.initialLoad && (!updatedAt || Date.now() / 1000 - updatedAt >= 120 * 120) && Date.now() / 1000 - loadingStartTimestamp < 30;
const isOffline = !isLoading && ((updatedAt && updatedAt < Date.now() / 1000 - 120 * 60) || (!updatedAt && Date.now() / 1000 - loadingStartTimestamp >= 30));
const isOffline = !isLoading && ((updatedAt && updatedAt < Date.now() / 1000 - 120 * 120) || (!updatedAt && Date.now() / 1000 - loadingStartTimestamp >= 30));
const isOnline = updatedAt && Date.now() / 1000 - updatedAt < 120 * 60;
const isOnline = updatedAt && Date.now() / 1000 - updatedAt < 120 * 120;
const offlineIconClass = isLoading ? 'yellowOfflineIcon' : isOffline ? 'redOfflineIcon' : '';
const offlineTitle = isLoading
+1 -1
View File
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { isAllView, isSubscriptionsView } from '../lib/utils/view-utils';
import useThemeStore from '../stores/use-theme-store';
import useDefaultSubplebbits from './use-default-subplebbits';
import { useDefaultSubplebbits } from './use-default-subplebbits';
import useInitialTheme from './use-initial-theme';
import { nsfwTags } from '../views/home/home';
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
+25
View File
@@ -0,0 +1,25 @@
import { create } from 'zustand';
interface AutoSubscribeState {
checkingAccounts: Set<string>;
addCheckingAccount: (address: string) => void;
removeCheckingAccount: (address: string) => void;
isCheckingAccount: (address: string) => boolean;
reset: () => void;
}
export const useAutoSubscribeStore = create<AutoSubscribeState>((set, get) => ({
checkingAccounts: new Set(),
addCheckingAccount: (address) =>
set((state) => ({
checkingAccounts: new Set(state.checkingAccounts).add(address),
})),
removeCheckingAccount: (address) =>
set((state) => {
const newSet = new Set(state.checkingAccounts);
newSet.delete(address);
return { checkingAccounts: newSet };
}),
isCheckingAccount: (address) => get().checkingAccounts.has(address),
reset: () => set({ checkingAccounts: new Set() }),
}));
+22 -13
View File
@@ -6,7 +6,6 @@ import { Trans, useTranslation } from 'react-i18next';
import styles from './board.module.css';
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import useFeedStateString from '../../hooks/use-feed-state-string';
import useReplyModal from '../../hooks/use-reply-modal';
import useTimeFilter from '../../hooks/use-time-filter';
import useFeedResetStore from '../../stores/use-feed-reset-store';
@@ -20,6 +19,7 @@ import SubplebbitRules from '../../components/subplebbit-rules';
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { shouldShowSnow } from '../../lib/snow';
import { useAutoSubscribe } from '../../hooks/use-auto-subscribe';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
@@ -31,6 +31,7 @@ const threadsWithoutImagesFilter = (comment: Comment) => {
};
const Board = () => {
const { isCheckingSubscriptions } = useAutoSubscribe();
const { t } = useTranslation();
const location = useLocation();
const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
@@ -295,18 +296,26 @@ const Board = () => {
/>
)}
{rules && !description && rules.length > 0 && <SubplebbitRules subplebbitAddress={subplebbitAddress} createdAt={createdAt} rules={rules} />}
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 1200 }}
totalCount={combinedFeed.length}
data={combinedFeed}
itemContent={(index, post) => <Post index={index} post={post} openReplyModal={openReplyModal} />}
useWindowScroll={true}
components={{ Footer }}
endReached={loadMore}
ref={virtuosoRef}
restoreStateFrom={lastVirtuosoState}
initialScrollTop={lastVirtuosoState?.scrollTop}
/>
{isCheckingSubscriptions ? (
<div className={styles.feed}>
<div className={styles.footer}>
<LoadingEllipsis string={t('loading_feed')} />
</div>
</div>
) : (
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 1200 }}
totalCount={combinedFeed.length}
data={combinedFeed}
itemContent={(index, post) => <Post index={index} post={post} openReplyModal={openReplyModal} />}
useWindowScroll={true}
components={{ Footer }}
endReached={loadMore}
ref={virtuosoRef}
restoreStateFrom={lastVirtuosoState}
initialScrollTop={lastVirtuosoState?.scrollTop}
/>
)}
</div>
</>
);
+1 -2
View File
@@ -6,8 +6,7 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
import useDefaultSubplebbits from '../../hooks/use-default-subplebbits';
import useFeedStateString from '../../hooks/use-feed-state-string';
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import useTimeFilter from '../../hooks/use-time-filter';
import useWindowWidth from '../../hooks/use-window-width';
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
+1 -1
View File
@@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { useSubplebbits } from '@plebbit/plebbit-react-hooks';
import styles from './home.module.css';
import packageJson from '../../../package.json';
import useDefaultSubplebbits, { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import { useDefaultSubplebbits, useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import useSubplebbitsStats from '../../hooks/use-subplebbits-stats';
import PopularThreadsBox from './popular-threads-box';
import BoardsList from './boards-list';