From f56594cc3206762ced56781aeb3d0d7e0840c51f Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Mon, 27 Apr 2026 19:26:58 +0700 Subject: [PATCH 1/2] chore: drop old plebbit list, rename to use-default-subscriptions Rename the hook file and exports from use-default-subplebbits to use-default-subscriptions. Switch the fetch URL from plebbit/lists default-multisub.json to bitsocialnet/lists seedit-default-subscriptions.json. A 404 from the new (not-yet-populated) list gracefully resolves to empty defaults so new users see an empty subscription feed (like a fresh Reddit account). The list will be populated later. --- .../author-sidebar/author-sidebar.tsx | 4 +- src/components/search-bar/search-bar.tsx | 4 +- src/components/topbar/topbar.tsx | 6 +- ...ebbits.ts => use-default-subscriptions.ts} | 79 ++++++++++--------- src/hooks/use-is-broadly-nsfw-community.ts | 4 +- src/hooks/use-is-nsfw-community.ts | 4 +- src/views/all/all.tsx | 4 +- src/views/communities/communities.tsx | 12 +-- src/views/domain/domain.tsx | 4 +- .../content-options/content-options.tsx | 4 +- src/views/submit-page/submit-page.tsx | 4 +- 11 files changed, 68 insertions(+), 61 deletions(-) rename src/hooks/{use-default-subplebbits.ts => use-default-subscriptions.ts} (56%) diff --git a/src/components/author-sidebar/author-sidebar.tsx b/src/components/author-sidebar/author-sidebar.tsx index de13a213..dee7af06 100644 --- a/src/components/author-sidebar/author-sidebar.tsx +++ b/src/components/author-sidebar/author-sidebar.tsx @@ -21,7 +21,7 @@ import { findAuthorCommunities, estimateAuthorKarma } from '../../lib/utils/user 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'; +import { useDefaultSubscriptionAddresses } from '../../hooks/use-default-subscriptions'; interface AuthorModeratingListProps { accountCommunities: Record>; @@ -70,7 +70,7 @@ const AuthorSidebar = () => { const { accountCommunities } = useAccountCommunities(); const profileOldestAccountTimestamp = getOldestAccountHistoryTimestamp(oldestAccountComment as { timestamp?: number }[]); - const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); + const defaultSubplebbitAddresses = useDefaultSubscriptionAddresses(); const accountSubscriptions = userAccount?.subscriptions || []; const subscriptionsAndDefaults = [...accountSubscriptions, ...defaultSubplebbitAddresses]; diff --git a/src/components/search-bar/search-bar.tsx b/src/components/search-bar/search-bar.tsx index 02e85886..7c7faad0 100644 --- a/src/components/search-bar/search-bar.tsx +++ b/src/components/search-bar/search-bar.tsx @@ -15,7 +15,7 @@ import { } 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 { useDefaultSubscriptionAddresses } from '../../hooks/use-default-subscriptions'; import styles from './search-bar.module.css'; import _ from 'lodash'; @@ -60,7 +60,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { const account = useAccount(); const subplebbitAddresses = useMemo(() => account?.subscriptions || [], [account?.subscriptions]); - const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); + const defaultSubplebbitAddresses = useDefaultSubscriptionAddresses(); const [isInputFocused, setIsInputFocused] = useState(false); const [activeDropdownIndex, setActiveDropdownIndex] = useState(-1); diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx index 8bcf7f49..c8302f55 100644 --- a/src/components/topbar/topbar.tsx +++ b/src/components/topbar/topbar.tsx @@ -5,7 +5,7 @@ import { useAccount, useAccountCommunities } from '@bitsocial/bitsocial-react-ho 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 { useDefaultSubscriptionAddresses, useDefaultSubscriptions } from '../../hooks/use-default-subscriptions'; import useTimeFilter, { setSessionTimeFilterPreference } from '../../hooks/use-time-filter'; import { sortTypes } from '../../constants/sort-types'; import { sortLabels } from '../../constants/sort-labels'; @@ -56,7 +56,7 @@ const CommunitiesDropdown = () => { const TagFilterDropdown = () => { const { t } = useTranslation(); const account = useAccount(); - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); const { hideAdultCommunities, hideGoreCommunities, @@ -291,7 +291,7 @@ const TopBar = memo(() => { const homeButtonClass = isInHomeView ? styles.selected : styles.choice; const { hideDefaultCommunities } = useContentOptionsStore(); - const communityAddresses = useDefaultSubplebbitAddresses(); + const communityAddresses = useDefaultSubscriptionAddresses(); const { accountCommunities } = useAccountCommunities(); const accountCommunityAddresses = useMemo(() => Object.keys(accountCommunities), [accountCommunities]); diff --git a/src/hooks/use-default-subplebbits.ts b/src/hooks/use-default-subscriptions.ts similarity index 56% rename from src/hooks/use-default-subplebbits.ts rename to src/hooks/use-default-subscriptions.ts index 24f2903b..4ee35f67 100644 --- a/src/hooks/use-default-subplebbits.ts +++ b/src/hooks/use-default-subscriptions.ts @@ -2,14 +2,14 @@ import { useEffect, useMemo, useState } from 'react'; import { Community } from '@bitsocial/bitsocial-react-hooks'; import useContentOptionsStore from '../stores/use-content-options-store'; -export interface MultisubMetadata { +export interface DefaultSubscriptionsMetadata { title: string; description: string; createdAt: number; updatedAt: number; } -export interface MultisubSubplebbit { +export interface DefaultSubscription { title?: string; address: string; tags?: string[]; @@ -19,8 +19,8 @@ export interface MultisubSubplebbit { lowUptime?: boolean; } -let cacheSubplebbits: MultisubSubplebbit[] | null = null; -let cacheMetadata: MultisubMetadata | null = null; +let cacheSubscriptions: DefaultSubscription[] | null = null; +let cacheMetadata: DefaultSubscriptionsMetadata | null = null; let cacheAutoSubscribeAddresses: string[] | null = null; let pending = false; @@ -32,26 +32,33 @@ const notifySubscribers = () => { }; // Shared fetch function to avoid duplication -const fetchMultisubData = async () => { +const fetchDefaultSubscriptionsData = async () => { if (pending) { return; } pending = true; try { - const multisub = await fetch( - 'https://raw.githubusercontent.com/plebbit/lists/master/default-multisub.json', - // { cache: 'no-cache' } - ).then((res) => res.json()); + const res = await fetch('https://raw.githubusercontent.com/bitsocialnet/lists/master/seedit-default-subscriptions.json'); - const filteredSubplebbits = multisub.subplebbits.filter((sub: MultisubSubplebbit) => !sub.lowUptime); + if (!res.ok) { + cacheSubscriptions = []; + cacheAutoSubscribeAddresses = []; + cacheMetadata = null; + notifySubscribers(); + return; + } + + const multisub = await res.json(); + + const filteredSubscriptions = multisub.subplebbits.filter((sub: DefaultSubscription) => !sub.lowUptime); - cacheSubplebbits = filteredSubplebbits; + cacheSubscriptions = filteredSubscriptions; - // Cache auto-subscribe addresses when we fetch subplebbits - cacheAutoSubscribeAddresses = filteredSubplebbits - .filter((sub: MultisubSubplebbit) => sub.seeditAutoSubscribe && sub.address) - .map((sub: MultisubSubplebbit) => sub.address); + // Cache auto-subscribe addresses when we fetch subscriptions + cacheAutoSubscribeAddresses = filteredSubscriptions + .filter((sub: DefaultSubscription) => sub.seeditAutoSubscribe && sub.address) + .map((sub: DefaultSubscription) => sub.address); // Also cache metadata since we have the full response const { title, description, createdAt, updatedAt } = multisub; @@ -60,7 +67,7 @@ const fetchMultisubData = async () => { // Notify all subscribers that cache has been updated notifySubscribers(); - return { subplebbits: filteredSubplebbits, metadata: cacheMetadata }; + return { subscriptions: filteredSubscriptions, metadata: cacheMetadata }; } catch (e) { console.warn(e); return null; @@ -69,20 +76,20 @@ const fetchMultisubData = async () => { } }; -export const useDefaultSubplebbits = () => { - const [subplebbits, setSubplebbits] = useState(cacheSubplebbits || []); +export const useDefaultSubscriptions = () => { + const [subscriptions, setSubscriptions] = useState(cacheSubscriptions || []); useEffect(() => { // If we already have cached data, use it immediately - if (cacheSubplebbits) { - setSubplebbits(cacheSubplebbits); + if (cacheSubscriptions) { + setSubscriptions(cacheSubscriptions); return; } // Subscribe to cache updates const handleCacheUpdate = () => { - if (cacheSubplebbits) { - setSubplebbits(cacheSubplebbits); + if (cacheSubscriptions) { + setSubscriptions(cacheSubscriptions); } }; @@ -90,7 +97,7 @@ export const useDefaultSubplebbits = () => { // Trigger fetch if no cache and not pending if (!pending) { - fetchMultisubData(); + fetchDefaultSubscriptionsData(); } // Cleanup subscription @@ -99,31 +106,31 @@ export const useDefaultSubplebbits = () => { }; }, []); - return subplebbits; + return subscriptions; }; export const getAutoSubscribeAddresses = () => cacheAutoSubscribeAddresses || []; -export const useDefaultSubplebbitAddresses = () => { - const defaultSubplebbits = useDefaultSubplebbits(); +export const useDefaultSubscriptionAddresses = () => { + const defaultSubscriptions = useDefaultSubscriptions(); const { hideAdultCommunities, hideGoreCommunities, hideAntiCommunities, hideVulgarCommunities } = useContentOptionsStore(); - const filteredSubplebbits = useMemo(() => { - return defaultSubplebbits.filter((subplebbit: Community) => { - const tags = subplebbit.tags || []; + const filteredSubscriptions = useMemo(() => { + return defaultSubscriptions.filter((subscription: Community) => { + const tags = subscription.tags || []; if (hideAdultCommunities && tags.includes('adult')) return false; if (hideGoreCommunities && tags.includes('gore')) return false; if (hideAntiCommunities && tags.includes('anti')) return false; if (hideVulgarCommunities && tags.includes('vulgar')) return false; return true; }); - }, [defaultSubplebbits, hideAdultCommunities, hideGoreCommunities, hideAntiCommunities, hideVulgarCommunities]); + }, [defaultSubscriptions, hideAdultCommunities, hideGoreCommunities, hideAntiCommunities, hideVulgarCommunities]); - return useMemo(() => filteredSubplebbits.map((subplebbit) => subplebbit.address), [filteredSubplebbits]); + return useMemo(() => filteredSubscriptions.map((subscription) => subscription.address), [filteredSubscriptions]); }; -export const useMultisubMetadata = () => { - const [metadata, setMetadata] = useState(cacheMetadata || null); +export const useDefaultSubscriptionsMetadata = () => { + const [metadata, setMetadata] = useState(cacheMetadata || null); useEffect(() => { // If we already have cached data, use it immediately @@ -143,7 +150,7 @@ export const useMultisubMetadata = () => { // Trigger fetch if no cache and not pending if (!pending) { - fetchMultisubData(); + fetchDefaultSubscriptionsData(); } // Cleanup subscription @@ -165,6 +172,6 @@ const getUniqueTags = (multisub: any) => { return Array.from(allTags).sort(); }; -export const useDefaultSubplebbitTags = (subplebbits: any) => { - return useMemo(() => getUniqueTags(subplebbits), [subplebbits]); +export const useDefaultSubscriptionTags = (subscriptions: any) => { + return useMemo(() => getUniqueTags(subscriptions), [subscriptions]); }; diff --git a/src/hooks/use-is-broadly-nsfw-community.ts b/src/hooks/use-is-broadly-nsfw-community.ts index 2682230c..95d6a688 100644 --- a/src/hooks/use-is-broadly-nsfw-community.ts +++ b/src/hooks/use-is-broadly-nsfw-community.ts @@ -1,10 +1,10 @@ import { useMemo } from 'react'; -import { useDefaultSubplebbits } from './use-default-subplebbits'; +import { useDefaultSubscriptions } from './use-default-subscriptions'; const SENSITIVE_TAGS = ['adult', 'gore', 'anti', 'vulgar']; export const useIsBroadlyNsfwCommunity = (communityAddress: string) => { - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); return useMemo(() => { if (!communityAddress || !defaultCommunities) return false; diff --git a/src/hooks/use-is-nsfw-community.ts b/src/hooks/use-is-nsfw-community.ts index ea8687be..26efae7b 100644 --- a/src/hooks/use-is-nsfw-community.ts +++ b/src/hooks/use-is-nsfw-community.ts @@ -1,8 +1,8 @@ import { useMemo } from 'react'; -import { useDefaultSubplebbits } from './use-default-subplebbits'; +import { useDefaultSubscriptions } from './use-default-subscriptions'; export const useIsNsfwCommunity = (communityAddress: string) => { - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); return useMemo(() => { if (!communityAddress || !defaultCommunities) return false; diff --git a/src/views/all/all.tsx b/src/views/all/all.tsx index f59ed0a1..e78a0b8c 100644 --- a/src/views/all/all.tsx +++ b/src/views/all/all.tsx @@ -5,7 +5,7 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { useFeed } from '@bitsocial/bitsocial-react-hooks'; import { commentMatchesPattern } from '../../lib/utils/pattern-utils'; import useFeedFiltersStore from '../../stores/use-feed-filters-store'; -import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; +import { useDefaultSubscriptionAddresses } from '../../hooks/use-default-subscriptions'; import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter'; import FeedFooter from '../../components/feed-footer'; import { getCommunityIdentifiers } from '../../hooks/use-community-identifier'; @@ -18,7 +18,7 @@ import styles from '../home/home.module.css'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; const All = () => { - const communityAddresses = useDefaultSubplebbitAddresses(); + const communityAddresses = useDefaultSubscriptionAddresses(); const params = useParams<{ sortType?: string; timeFilterName?: string }>(); const [searchParams, setSearchParams] = useSearchParams(); const searchQuery = searchParams.get('q') || ''; diff --git a/src/views/communities/communities.tsx b/src/views/communities/communities.tsx index ae1490e9..2a7285b0 100644 --- a/src/views/communities/communities.tsx +++ b/src/views/communities/communities.tsx @@ -16,7 +16,7 @@ import { } 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 { useDefaultSubscriptionAddresses, useDefaultSubscriptions } from '../../hooks/use-default-subscriptions'; import useDisplayedSubscriptions from '../../hooks/use-displayed-subscriptions'; import useIsMobile from '../../hooks/use-is-mobile'; import useIsCommunityOffline from '../../hooks/use-is-community-offline'; @@ -314,7 +314,7 @@ const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => { const { accountCommunities, error: accountCommunitiesError } = useAccountCommunities(); const { setError } = useErrorStore(); const location = useLocation(); - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); useEffect(() => { setError('AccountSubplebbits_useAccountCommunities', accountCommunitiesError); @@ -356,7 +356,7 @@ const SubscriberSubplebbits = () => { const account = useAccount(); const { setError } = useErrorStore(); const location = useLocation(); - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); const urlParams = new URLSearchParams(location.search); const currentTag = urlParams.get('tag'); @@ -416,8 +416,8 @@ const SubscriberSubplebbits = () => { }; const AllDefaultSubplebbits = () => { - const defaultCommunitiesList = useDefaultSubplebbits(); - const communityAddresses = useDefaultSubplebbitAddresses(); + const defaultCommunitiesList = useDefaultSubscriptions(); + const communityAddresses = useDefaultSubscriptionAddresses(); const location = useLocation(); const urlParams = new URLSearchParams(location.search); @@ -460,7 +460,7 @@ const AllAccountSubplebbits = () => { const { accountCommunities, error: accountCommunitiesError } = useAccountCommunities(); const { setError } = useErrorStore(); const location = useLocation(); - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); useEffect(() => { setError('AllAccountSubplebbits_useAccountCommunities', accountCommunitiesError); diff --git a/src/views/domain/domain.tsx b/src/views/domain/domain.tsx index 6f953ad1..cff3eaca 100644 --- a/src/views/domain/domain.tsx +++ b/src/views/domain/domain.tsx @@ -4,7 +4,7 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { useFeed, Comment, CommentsFilter } from '@bitsocial/bitsocial-react-hooks'; import { commentMatchesPattern } from '../../lib/utils/pattern-utils'; import useFeedFiltersStore from '../../stores/use-feed-filters-store'; -import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; +import { useDefaultSubscriptionAddresses } from '../../hooks/use-default-subscriptions'; import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter'; import FeedFooter from '../../components/feed-footer'; import { getCommunityIdentifiers } from '../../hooks/use-community-identifier'; @@ -17,7 +17,7 @@ import { sortTypes } from '../../constants/sort-types'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; const Domain = () => { - const communityAddresses = useDefaultSubplebbitAddresses(); + const communityAddresses = useDefaultSubscriptionAddresses(); const params = useParams<{ domain?: string; sortType?: string; timeFilterName?: string }>(); const [searchParams, setSearchParams] = useSearchParams(); const navigate = useNavigate(); diff --git a/src/views/settings/content-options/content-options.tsx b/src/views/settings/content-options/content-options.tsx index 48cdd6b0..65fbd74f 100644 --- a/src/views/settings/content-options/content-options.tsx +++ b/src/views/settings/content-options/content-options.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'; import { useAccount } from '@bitsocial/bitsocial-react-hooks'; import styles from './content-options.module.css'; import useContentOptionsStore from '../../../stores/use-content-options-store'; -import { useDefaultSubplebbits } from '../../../hooks/use-default-subplebbits'; +import { useDefaultSubscriptions } from '../../../hooks/use-default-subscriptions'; import { handleNSFWSubscriptionPrompt } from '../../../lib/utils/nsfw-subscription-utils'; const MediaOptions = () => { @@ -138,7 +138,7 @@ const MediaOptions = () => { const CommunitiesOptions = () => { const { t } = useTranslation(); const account = useAccount(); - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); const { hideAdultCommunities, hideGoreCommunities, diff --git a/src/views/submit-page/submit-page.tsx b/src/views/submit-page/submit-page.tsx index 1c83ed15..46861057 100644 --- a/src/views/submit-page/submit-page.tsx +++ b/src/views/submit-page/submit-page.tsx @@ -9,7 +9,7 @@ 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 { useDefaultSubscriptionAddresses } from '../../hooks/use-default-subscriptions'; import useIsCommunityOffline from '../../hooks/use-is-community-offline'; import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; import LoadingEllipsis from '../../components/loading-ellipsis'; @@ -308,7 +308,7 @@ const ContentField = () => { const CommunityAddressField = () => { const { t } = useTranslation(); const { subscriptions } = useAccount() || {}; - const defaultCommunityAddresses = useDefaultSubplebbitAddresses(); + const defaultCommunityAddresses = useDefaultSubscriptionAddresses(); const { communityAddress: inputAddress, setPublishPostStore } = usePublishPostStore(); const filteredCommunityAddresses = defaultCommunityAddresses.filter((address) => address?.toLowerCase()?.includes(inputAddress?.toLowerCase() || '')).slice(0, 10); From 0da6e32decb4698c739de2c2ecff85324524522f Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Mon, 27 Apr 2026 19:27:00 +0700 Subject: [PATCH 2/2] fix(default-subscriptions): unblock home when default list is empty When use-default-subscriptions resolves to [] (the expected case while seedit-default-subscriptions.json is empty or 404s), the auto-subscribe effect was early-returning before removing the account from the checking set. That kept isCheckingSubscriptions true forever and prevented the "no subscriptions" empty state from ever rendering on home. Fix by ensuring removeCheckingAccount is always called once the auto-subscribe pass completes, even when there is nothing to auto-subscribe to. --- src/hooks/use-auto-subscribe.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hooks/use-auto-subscribe.ts b/src/hooks/use-auto-subscribe.ts index d999a09d..f56bdf56 100644 --- a/src/hooks/use-auto-subscribe.ts +++ b/src/hooks/use-auto-subscribe.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { useAccount, setAccount } from '@bitsocial/bitsocial-react-hooks'; -import { getAutoSubscribeAddresses, useDefaultSubplebbits } from './use-default-subplebbits'; +import { getAutoSubscribeAddresses, useDefaultSubscriptions } from './use-default-subscriptions'; import { useAutoSubscribeStore } from '../stores/use-auto-subscribe-store'; const AUTO_SUBSCRIBE_KEY_PREFIX = 'seedit-auto-subscribe-done-'; @@ -11,7 +11,7 @@ const processedAccounts = new Set(); export const useAutoSubscribe = () => { const account = useAccount(); const accountAddress = account?.author?.address; - const defaultCommunities = useDefaultSubplebbits(); + const defaultCommunities = useDefaultSubscriptions(); const { addCheckingAccount, removeCheckingAccount, isCheckingAccount } = useAutoSubscribeStore(); useEffect(() => { @@ -21,7 +21,10 @@ export const useAutoSubscribe = () => { addCheckingAccount(accountAddress); const processAutoSubscribe = async () => { - if (!account || !defaultCommunities?.length) return; + if (!account || !defaultCommunities?.length) { + removeCheckingAccount(accountAddress); + return; + } if (processedAccounts.has(accountAddress)) { removeCheckingAccount(accountAddress);