diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx
index 27dc6436..6ac598db 100644
--- a/src/components/topbar/topbar.tsx
+++ b/src/components/topbar/topbar.tsx
@@ -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) => (
-
- {index === 0 ? null : ' '}
-
- {sub.address.endsWith('.eth') || sub.address.endsWith('.sol') ? sub.address : sub.address.slice(0, 10).concat('...')}
-
- {index !== subplebbits.length - 1 ? ' /' : null}
-
- ))}
- ]{' '}
- {subscriptions?.length > 0 && (
- <>
- [
- {subscriptions.map((address: any, index: any) => (
-
- {index === 0 ? null : ' '}
-
- {address.endsWith('.eth') || address.endsWith('.sol') ? address : address.slice(0, 10).concat('...')}
-
- {index !== subscriptions?.length - 1 ? ' /' : null}
-
- ))}
- ]{' '}
- >
- )}
- >
- );
-
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 (
[all / subscriptions]{' '}
- {renderBoardsList(subplebbits.slice(0, 15), isInCatalogView, account?.subscriptions)}[
+ {subscriptions?.length > 0 && (
+ <>
+ [
+ {subscriptions.map((address: any, index: any) => (
+
+ {index === 0 ? null : ' '}
+
+ {address.endsWith('.eth') || address.endsWith('.sol') ? address : address.slice(0, 10).concat('...')}
+
+ {index !== subscriptions?.length - 1 ? ' /' : null}
+
+ ))}
+ ]{' '}
+ >
+ )}
+ [
();
+
+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 };
+};
diff --git a/src/hooks/use-default-subplebbits.ts b/src/hooks/use-default-subplebbits.ts
index f7aaa81e..d604862e 100644
--- a/src/hooks/use-default-subplebbits.ts
+++ b/src/hooks/use-default-subplebbits.ts
@@ -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([]);
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 || [];
diff --git a/src/hooks/use-initial-theme.ts b/src/hooks/use-initial-theme.ts
index 9f041663..337ba05d 100644
--- a/src/hooks/use-initial-theme.ts
+++ b/src/hooks/use-initial-theme.ts
@@ -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';
diff --git a/src/hooks/use-is-subplebbit-offline.ts b/src/hooks/use-is-subplebbit-offline.ts
index f0a98838..50a80023 100644
--- a/src/hooks/use-is-subplebbit-offline.ts
+++ b/src/hooks/use-is-subplebbit-offline.ts
@@ -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
diff --git a/src/hooks/use-theme.ts b/src/hooks/use-theme.ts
index 346998c0..ac04bc1f 100644
--- a/src/hooks/use-theme.ts
+++ b/src/hooks/use-theme.ts
@@ -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';
diff --git a/src/stores/use-auto-subscribe-store.ts b/src/stores/use-auto-subscribe-store.ts
new file mode 100644
index 00000000..c65bceb1
--- /dev/null
+++ b/src/stores/use-auto-subscribe-store.ts
@@ -0,0 +1,25 @@
+import { create } from 'zustand';
+
+interface AutoSubscribeState {
+ checkingAccounts: Set;
+ addCheckingAccount: (address: string) => void;
+ removeCheckingAccount: (address: string) => void;
+ isCheckingAccount: (address: string) => boolean;
+ reset: () => void;
+}
+
+export const useAutoSubscribeStore = create((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() }),
+}));
diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx
index 9f353f5a..2a62f38b 100644
--- a/src/views/board/board.tsx
+++ b/src/views/board/board.tsx
@@ -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 && }
- }
- useWindowScroll={true}
- components={{ Footer }}
- endReached={loadMore}
- ref={virtuosoRef}
- restoreStateFrom={lastVirtuosoState}
- initialScrollTop={lastVirtuosoState?.scrollTop}
- />
+ {isCheckingSubscriptions ? (
+
+ ) : (
+ }
+ useWindowScroll={true}
+ components={{ Footer }}
+ endReached={loadMore}
+ ref={virtuosoRef}
+ restoreStateFrom={lastVirtuosoState}
+ initialScrollTop={lastVirtuosoState?.scrollTop}
+ />
+ )}
>
);
diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx
index cc612419..79d0f3d4 100644
--- a/src/views/catalog/catalog.tsx
+++ b/src/views/catalog/catalog.tsx
@@ -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';
diff --git a/src/views/home/home.tsx b/src/views/home/home.tsx
index 4d592220..83af6cf3 100644
--- a/src/views/home/home.tsx
+++ b/src/views/home/home.tsx
@@ -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';