mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat: auto subscribe new accounts to specific default subplebbits
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
@@ -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,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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user