mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
perf(mod queue): stabilize moderation subscriptions
This commit is contained in:
@@ -9,6 +9,7 @@ import { useBoardFeedPageSize } from '../use-board-feed-page-size';
|
|||||||
import { useBoardPseudonymityMode } from '../use-board-pseudonymity-mode';
|
import { useBoardPseudonymityMode } from '../use-board-pseudonymity-mode';
|
||||||
import useCountLinksInReplies from '../use-count-links-in-replies';
|
import useCountLinksInReplies from '../use-count-links-in-replies';
|
||||||
import { useFilteredDirectoryAddresses } from '../use-filtered-directory-addresses';
|
import { useFilteredDirectoryAddresses } from '../use-filtered-directory-addresses';
|
||||||
|
import { useModeratedCommunityAddressInputs, useModeratedCommunityAddressesForInputs } from '../use-moderated-community-addresses';
|
||||||
import useAllFeedFilterStore from '../../stores/use-all-feed-filter-store';
|
import useAllFeedFilterStore from '../../stores/use-all-feed-filter-store';
|
||||||
|
|
||||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
@@ -17,6 +18,7 @@ const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise
|
|||||||
const testState = vi.hoisted(() => ({
|
const testState = vi.hoisted(() => ({
|
||||||
account: undefined as unknown,
|
account: undefined as unknown,
|
||||||
accountCommunities: {} as Record<string, unknown>,
|
accountCommunities: {} as Record<string, unknown>,
|
||||||
|
liveCommunities: {} as Record<string, unknown>,
|
||||||
directories: [] as Array<{ address: string; nsfw?: boolean }>,
|
directories: [] as Array<{ address: string; nsfw?: boolean }>,
|
||||||
directoryLookup: {} as Record<string, unknown>,
|
directoryLookup: {} as Record<string, unknown>,
|
||||||
flattenedReplies: [] as unknown[],
|
flattenedReplies: [] as unknown[],
|
||||||
@@ -35,13 +37,14 @@ vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
|||||||
|
|
||||||
vi.mock('@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js', () => ({
|
vi.mock('@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js', () => ({
|
||||||
default: (
|
default: (
|
||||||
selector: (state: { activeAccountId?: string; accounts: Record<string, { communities?: typeof testState.accountCommunities }> }) => unknown,
|
selector: (state: { activeAccountId?: string; accounts: Record<string, Record<string, unknown> & { communities?: typeof testState.accountCommunities }> }) => unknown,
|
||||||
equalityFn?: (previous: unknown, next: unknown) => boolean,
|
equalityFn?: (previous: unknown, next: unknown) => boolean,
|
||||||
) => {
|
) => {
|
||||||
const nextValue = selector({
|
const nextValue = selector({
|
||||||
activeAccountId: 'active',
|
activeAccountId: 'active',
|
||||||
accounts: {
|
accounts: {
|
||||||
active: {
|
active: {
|
||||||
|
...(testState.account as Record<string, unknown> | undefined),
|
||||||
communities: testState.accountCommunities,
|
communities: testState.accountCommunities,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -57,6 +60,27 @@ vi.mock('@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js', () =>
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const communitiesStoreSelectorCache = vi.hoisted(() => ({
|
||||||
|
hasValue: false,
|
||||||
|
value: undefined as unknown,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@bitsocial/bitsocial-react-hooks/dist/stores/communities', () => ({
|
||||||
|
default: (selector: (state: { communities: Record<string, unknown> }) => unknown, equalityFn?: (previous: unknown, next: unknown) => boolean) => {
|
||||||
|
const nextValue = selector({
|
||||||
|
communities: testState.liveCommunities,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (communitiesStoreSelectorCache.hasValue && equalityFn?.(communitiesStoreSelectorCache.value, nextValue)) {
|
||||||
|
return communitiesStoreSelectorCache.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
communitiesStoreSelectorCache.hasValue = true;
|
||||||
|
communitiesStoreSelectorCache.value = nextValue;
|
||||||
|
return nextValue;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
vi.mock('@bitsocial/bitsocial-react-hooks/dist/lib/community-address.js', () => ({
|
vi.mock('@bitsocial/bitsocial-react-hooks/dist/lib/community-address.js', () => ({
|
||||||
getEquivalentCommunityAddressGroupKey: (address: string) => (address.endsWith('.eth') ? address.slice(0, -4) + '.bso' : address),
|
getEquivalentCommunityAddressGroupKey: (address: string) => (address.endsWith('.eth') ? address.slice(0, -4) + '.bso' : address),
|
||||||
pickPreferredEquivalentCommunityAddress: (addresses: string[]) => addresses.find((address) => address.endsWith('.bso')) || addresses[0],
|
pickPreferredEquivalentCommunityAddress: (addresses: string[]) => addresses.find((address) => address.endsWith('.bso')) || addresses[0],
|
||||||
@@ -67,6 +91,7 @@ vi.mock('@bitsocial/bitsocial-react-hooks/dist/lib/utils', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../use-directories', () => ({
|
vi.mock('../use-directories', () => ({
|
||||||
|
normalizeBoardAddress: (address: string | undefined) => address?.toLowerCase() ?? '',
|
||||||
useDirectories: () => testState.directories,
|
useDirectories: () => testState.directories,
|
||||||
useDirectoryByAddress: (address: string | undefined) => (address ? testState.directoryLookup[address] : undefined),
|
useDirectoryByAddress: (address: string | undefined) => (address ? testState.directoryLookup[address] : undefined),
|
||||||
}));
|
}));
|
||||||
@@ -110,12 +135,15 @@ describe('selector hooks', () => {
|
|||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
testState.account = undefined;
|
testState.account = undefined;
|
||||||
testState.accountCommunities = {};
|
testState.accountCommunities = {};
|
||||||
|
testState.liveCommunities = {};
|
||||||
testState.directories = [];
|
testState.directories = [];
|
||||||
testState.directoryLookup = {};
|
testState.directoryLookup = {};
|
||||||
testState.flattenedReplies = [];
|
testState.flattenedReplies = [];
|
||||||
testState.communitySnapshot = undefined;
|
testState.communitySnapshot = undefined;
|
||||||
accountsStoreSelectorCache.hasValue = false;
|
accountsStoreSelectorCache.hasValue = false;
|
||||||
accountsStoreSelectorCache.value = undefined;
|
accountsStoreSelectorCache.value = undefined;
|
||||||
|
communitiesStoreSelectorCache.hasValue = false;
|
||||||
|
communitiesStoreSelectorCache.value = undefined;
|
||||||
useAllFeedFilterStore.getState().setFilter('all');
|
useAllFeedFilterStore.getState().setFilter('all');
|
||||||
|
|
||||||
container = document.createElement('div');
|
container = document.createElement('div');
|
||||||
@@ -166,6 +194,65 @@ describe('selector hooks', () => {
|
|||||||
expect(addressesWithNewBoard).toEqual(['biz.eth', 'music.eth', 'tech.eth']);
|
expect(addressesWithNewBoard).toEqual(['biz.eth', 'music.eth', 'tech.eth']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps moderated board address identity stable when transient community state changes', () => {
|
||||||
|
testState.account = {
|
||||||
|
author: { address: '0xme' },
|
||||||
|
subscriptions: ['sub.eth'],
|
||||||
|
};
|
||||||
|
testState.accountCommunities = {
|
||||||
|
'owned.eth': { address: 'owned.eth', state: 'updating' },
|
||||||
|
};
|
||||||
|
testState.directories = [{ address: 'dir.eth' }, { address: 'sub.eth' }];
|
||||||
|
testState.liveCommunities = {
|
||||||
|
'dir.eth': {
|
||||||
|
address: 'dir.eth',
|
||||||
|
state: 'fetching-ipns',
|
||||||
|
roles: { '0xme': { role: 'moderator' } },
|
||||||
|
},
|
||||||
|
'sub.eth': {
|
||||||
|
address: 'sub.eth',
|
||||||
|
state: 'fetching-ipns',
|
||||||
|
roles: { '0xme': { role: 'member' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const useModeratedAddresses = () => {
|
||||||
|
const inputs = useModeratedCommunityAddressInputs();
|
||||||
|
return useModeratedCommunityAddressesForInputs(inputs);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialAddresses = rerenderHookValue(useModeratedAddresses);
|
||||||
|
expect(initialAddresses).toEqual(['owned.eth', 'dir.eth']);
|
||||||
|
|
||||||
|
testState.liveCommunities = {
|
||||||
|
'dir.eth': {
|
||||||
|
address: 'dir.eth',
|
||||||
|
state: 'succeeded',
|
||||||
|
roles: { '0xme': { role: 'moderator' } },
|
||||||
|
},
|
||||||
|
'sub.eth': {
|
||||||
|
address: 'sub.eth',
|
||||||
|
state: 'succeeded',
|
||||||
|
roles: { '0xme': { role: 'member' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(rerenderHookValue(useModeratedAddresses)).toBe(initialAddresses);
|
||||||
|
|
||||||
|
testState.liveCommunities = {
|
||||||
|
...testState.liveCommunities,
|
||||||
|
'sub.eth': {
|
||||||
|
address: 'sub.eth',
|
||||||
|
state: 'succeeded',
|
||||||
|
roles: { '0xme': { role: 'admin' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const addressesWithNewRole = rerenderHookValue(useModeratedAddresses);
|
||||||
|
expect(addressesWithNewRole).not.toBe(initialAddresses);
|
||||||
|
expect(addressesWithNewRole).toEqual(['owned.eth', 'dir.eth', 'sub.eth']);
|
||||||
|
});
|
||||||
|
|
||||||
it('computes moderator privileges and whether the current account authored the comment', () => {
|
it('computes moderator privileges and whether the current account authored the comment', () => {
|
||||||
testState.account = { author: { address: '0xme' } };
|
testState.account = { author: { address: '0xme' } };
|
||||||
testState.communitySnapshot = {
|
testState.communitySnapshot = {
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ type AccountsStoreState = {
|
|||||||
|
|
||||||
const EMPTY_ACCOUNT_COMMUNITY_ADDRESSES: string[] = [];
|
const EMPTY_ACCOUNT_COMMUNITY_ADDRESSES: string[] = [];
|
||||||
|
|
||||||
const areStringArraysEqual = (previous: string[], next: string[]) => {
|
export const areStringArraysEqual = (previous: readonly string[] | undefined, next: readonly string[] | undefined) => {
|
||||||
if (previous === next) {
|
if (previous === next) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!previous || !next) {
|
||||||
|
return previous === next;
|
||||||
|
}
|
||||||
if (previous.length !== next.length) {
|
if (previous.length !== next.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,58 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useAccount, useCommunities } from '@bitsocial/bitsocial-react-hooks';
|
import type { Community } from '@bitsocial/bitsocial-react-hooks';
|
||||||
|
import useAccountsStore from '@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js';
|
||||||
|
import useCommunitiesStore from '@bitsocial/bitsocial-react-hooks/dist/stores/communities';
|
||||||
import { useDirectories } from './use-directories';
|
import { useDirectories } from './use-directories';
|
||||||
import { useCommunityIdentifiers } from './use-community-identifiers';
|
import { areStringArraysEqual, useAccountCommunityAddresses } from './use-account-community-addresses';
|
||||||
import { useAccountCommunityAddresses } from './use-account-community-addresses';
|
|
||||||
import { getModeratedCommunityAddresses } from '../lib/utils/mod-queue-utils';
|
import { getModeratedCommunityAddresses } from '../lib/utils/mod-queue-utils';
|
||||||
|
import { normalizeBoardAddress } from '../lib/utils/directory-list-lookup-utils';
|
||||||
|
|
||||||
|
type AccountModerationSnapshot = {
|
||||||
|
accountAddress: string | undefined;
|
||||||
|
subscriptions: readonly string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccountWithModerationFields = {
|
||||||
|
author?: {
|
||||||
|
address?: string;
|
||||||
|
};
|
||||||
|
subscriptions?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccountsStoreState = {
|
||||||
|
activeAccountId?: string;
|
||||||
|
accounts: Record<string, AccountWithModerationFields | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CommunitiesStoreState = {
|
||||||
|
communities: Record<string, Community | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ModeratedCommunityAddressInputs = {
|
||||||
|
accountAddress: string | undefined;
|
||||||
|
accountCommunityAddresses: string[];
|
||||||
|
candidateCommunityAddresses: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_SUBSCRIPTIONS: readonly string[] = [];
|
||||||
|
|
||||||
|
const areAccountModerationSnapshotsEqual = (previous: AccountModerationSnapshot | undefined, next: AccountModerationSnapshot | undefined) => {
|
||||||
|
if (previous === next) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!previous || !next) {
|
||||||
|
return previous === next;
|
||||||
|
}
|
||||||
|
return previous.accountAddress === next.accountAddress && areStringArraysEqual(previous.subscriptions, next.subscriptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAccountModerationSnapshot = (state: AccountsStoreState): AccountModerationSnapshot => {
|
||||||
|
const account = state.activeAccountId ? state.accounts[state.activeAccountId] : undefined;
|
||||||
|
return {
|
||||||
|
accountAddress: account?.author?.address,
|
||||||
|
subscriptions: account?.subscriptions ?? EMPTY_SUBSCRIPTIONS,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const addUniqueAddress = (addresses: string[], address: string | undefined) => {
|
const addUniqueAddress = (addresses: string[], address: string | undefined) => {
|
||||||
if (address && !addresses.includes(address)) {
|
if (address && !addresses.includes(address)) {
|
||||||
@@ -11,9 +60,21 @@ const addUniqueAddress = (addresses: string[], address: string | undefined) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useModeratedCommunityAddresses = (): string[] => {
|
const getCommunityByAddress = (communities: Record<string, Community | undefined>, communityAddress: string): Community | undefined => {
|
||||||
const account = useAccount();
|
const exactMatch = communities[communityAddress];
|
||||||
const accountAddress = account?.author?.address;
|
if (exactMatch) {
|
||||||
|
return exactMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedAddress = normalizeBoardAddress(communityAddress);
|
||||||
|
return Object.entries(communities).find(([key, community]) => {
|
||||||
|
const candidateAddress = typeof community?.address === 'string' ? community.address : key;
|
||||||
|
return normalizeBoardAddress(candidateAddress) === normalizedAddress;
|
||||||
|
})?.[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useModeratedCommunityAddressInputs = (): ModeratedCommunityAddressInputs => {
|
||||||
|
const { accountAddress, subscriptions } = useAccountsStore(getAccountModerationSnapshot, areAccountModerationSnapshotsEqual);
|
||||||
const accountCommunityAddresses = useAccountCommunityAddresses();
|
const accountCommunityAddresses = useAccountCommunityAddresses();
|
||||||
const directories = useDirectories();
|
const directories = useDirectories();
|
||||||
|
|
||||||
@@ -25,23 +86,39 @@ export const useModeratedCommunityAddresses = (): string[] => {
|
|||||||
for (const directory of directories) {
|
for (const directory of directories) {
|
||||||
addUniqueAddress(addresses, directory.address);
|
addUniqueAddress(addresses, directory.address);
|
||||||
}
|
}
|
||||||
for (const address of account?.subscriptions ?? []) {
|
for (const address of subscriptions) {
|
||||||
addUniqueAddress(addresses, address);
|
addUniqueAddress(addresses, address);
|
||||||
}
|
}
|
||||||
return addresses;
|
return addresses;
|
||||||
}, [account?.subscriptions, accountCommunityAddresses, directories]);
|
}, [accountCommunityAddresses, directories, subscriptions]);
|
||||||
|
|
||||||
const candidateCommunities = useCommunityIdentifiers(candidateCommunityAddresses);
|
|
||||||
const { communities } = useCommunities(candidateCommunities.length > 0 ? { communities: candidateCommunities } : undefined);
|
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() => ({
|
||||||
|
accountAddress,
|
||||||
|
accountCommunityAddresses,
|
||||||
|
candidateCommunityAddresses,
|
||||||
|
}),
|
||||||
|
[accountAddress, accountCommunityAddresses, candidateCommunityAddresses],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useModeratedCommunityAddressesForInputs = ({
|
||||||
|
accountAddress,
|
||||||
|
accountCommunityAddresses,
|
||||||
|
candidateCommunityAddresses,
|
||||||
|
}: ModeratedCommunityAddressInputs): string[] =>
|
||||||
|
useCommunitiesStore(
|
||||||
|
(state: CommunitiesStoreState) =>
|
||||||
getModeratedCommunityAddresses({
|
getModeratedCommunityAddresses({
|
||||||
accountAddress,
|
accountAddress,
|
||||||
accountCommunityAddresses,
|
accountCommunityAddresses,
|
||||||
candidateCommunityAddresses,
|
candidateCommunityAddresses,
|
||||||
communities,
|
communities: candidateCommunityAddresses.map((candidateAddress) => getCommunityByAddress(state.communities, candidateAddress)),
|
||||||
}),
|
}),
|
||||||
[accountAddress, accountCommunityAddresses, candidateCommunityAddresses, communities],
|
areStringArraysEqual,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const useModeratedCommunityAddresses = (): string[] => {
|
||||||
|
const inputs = useModeratedCommunityAddressInputs();
|
||||||
|
return useModeratedCommunityAddressesForInputs(inputs);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useMemo, useState, useEffect, useCallback, memo } from 'react';
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams, Link } from 'react-router-dom';
|
import { useParams, Link } from 'react-router-dom';
|
||||||
import { useFeed, Comment, usePublishCommentModeration, useEditedComment, useCommunity, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
import { useFeed, Comment, usePublishCommentModeration, useEditedComment, useCommunity, useCommunities } from '@bitsocial/bitsocial-react-hooks';
|
||||||
import useAccountsStore from '@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js';
|
import useAccountsStore from '@bitsocial/bitsocial-react-hooks/dist/stores/accounts/index.js';
|
||||||
import { useFloating, offset, shift, size, flip, autoUpdate } from '@floating-ui/react';
|
import { useFloating, offset, shift, size, flip, autoUpdate } from '@floating-ui/react';
|
||||||
import { Virtuoso, type Components } from 'react-virtuoso';
|
import { Virtuoso, type Components } from 'react-virtuoso';
|
||||||
@@ -49,7 +49,7 @@ import capitalize from 'lodash/capitalize';
|
|||||||
import lowerCase from 'lodash/lowerCase';
|
import lowerCase from 'lodash/lowerCase';
|
||||||
import { PageFooterDesktop, PageFooterMobile, StyleOnlyFooterFirstRow } from '../../components/footer/footer';
|
import { PageFooterDesktop, PageFooterMobile, StyleOnlyFooterFirstRow } from '../../components/footer/footer';
|
||||||
import footerStyles from '../../components/footer/footer.module.css';
|
import footerStyles from '../../components/footer/footer.module.css';
|
||||||
import { useModeratedCommunityAddresses } from '../../hooks/use-moderated-community-addresses';
|
import { useModeratedCommunityAddressInputs, useModeratedCommunityAddressesForInputs } from '../../hooks/use-moderated-community-addresses';
|
||||||
|
|
||||||
/** Path for display: directory code, or full address if has TLD, or shortened for long IPNS keys (no dot) */
|
/** Path for display: directory code, or full address if has TLD, or shortened for long IPNS keys (no dot) */
|
||||||
const getBoardDisplayPath = (address: string, path: string): string => {
|
const getBoardDisplayPath = (address: string, path: string): string => {
|
||||||
@@ -124,6 +124,12 @@ const EMPTY_COMMENTS: Comment[] = [];
|
|||||||
const MOD_QUEUE_VIRTUOSO_INCREASE_VIEWPORT_BY = { bottom: 600, top: 600 };
|
const MOD_QUEUE_VIRTUOSO_INCREASE_VIEWPORT_BY = { bottom: 600, top: 600 };
|
||||||
const NOOP_LOAD_MORE = () => undefined;
|
const NOOP_LOAD_MORE = () => undefined;
|
||||||
|
|
||||||
|
const ModQueueCommunityMetadataLoader = memo(({ candidateCommunityAddresses }: { candidateCommunityAddresses: string[] }) => {
|
||||||
|
const candidateCommunities = useCommunityIdentifiers(candidateCommunityAddresses);
|
||||||
|
useCommunities(candidateCommunities.length > 0 ? { communities: candidateCommunities } : undefined);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
interface ModQueueFooterProps {
|
interface ModQueueFooterProps {
|
||||||
hasMore: boolean;
|
hasMore: boolean;
|
||||||
loadingStateString: string;
|
loadingStateString: string;
|
||||||
@@ -1119,9 +1125,9 @@ const ModQueueButtonContent = ({ feed, alertThresholdSeconds, boardIdentifier, i
|
|||||||
export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProps) => {
|
export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProps) => {
|
||||||
const getAlertThresholdSeconds = useModQueueStore((state) => state.getAlertThresholdSeconds);
|
const getAlertThresholdSeconds = useModQueueStore((state) => state.getAlertThresholdSeconds);
|
||||||
|
|
||||||
const account = useAccount();
|
const moderatedCommunityAddressInputs = useModeratedCommunityAddressInputs();
|
||||||
const accountAddress = account?.author?.address;
|
const accountAddress = moderatedCommunityAddressInputs.accountAddress;
|
||||||
const rawAccountCommunityAddresses = useModeratedCommunityAddresses();
|
const rawAccountCommunityAddresses = useModeratedCommunityAddressesForInputs(moderatedCommunityAddressInputs);
|
||||||
const accountCommunityAddressesKey = getAddressListKey(rawAccountCommunityAddresses);
|
const accountCommunityAddressesKey = getAddressListKey(rawAccountCommunityAddresses);
|
||||||
const accountCommunityAddresses = useMemo(() => getAddressListFromKey(accountCommunityAddressesKey), [accountCommunityAddressesKey]);
|
const accountCommunityAddresses = useMemo(() => getAddressListFromKey(accountCommunityAddressesKey), [accountCommunityAddressesKey]);
|
||||||
|
|
||||||
@@ -1175,15 +1181,21 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp
|
|||||||
[feedCommunities],
|
[feedCommunities],
|
||||||
);
|
);
|
||||||
const { feed } = useFeed(feedOptions);
|
const { feed } = useFeed(feedOptions);
|
||||||
|
const metadataLoader = <ModQueueCommunityMetadataLoader candidateCommunityAddresses={moderatedCommunityAddressInputs.candidateCommunityAddresses} />;
|
||||||
|
|
||||||
if (!shouldFetch || communityAddresses.length === 0) {
|
if (!shouldFetch || communityAddresses.length === 0) {
|
||||||
return null;
|
return metadataLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||||
// Remount when switching boards so memoized counts reset cleanly.
|
// Remount when switching boards so memoized counts reset cleanly.
|
||||||
const contentKey = communityAddresses.join(',');
|
const contentKey = communityAddresses.join(',');
|
||||||
return <ModQueueButtonContent key={contentKey} feed={feed} alertThresholdSeconds={alertThresholdSeconds} boardIdentifier={boardIdentifier} isMobile={isMobile} />;
|
return (
|
||||||
|
<>
|
||||||
|
{metadataLoader}
|
||||||
|
<ModQueueButtonContent key={contentKey} feed={feed} alertThresholdSeconds={alertThresholdSeconds} boardIdentifier={boardIdentifier} isMobile={isMobile} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
||||||
@@ -1195,7 +1207,8 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp
|
|||||||
const rememberCommentsInQueue = useModQueueStore((state) => state.rememberCommentsInQueue);
|
const rememberCommentsInQueue = useModQueueStore((state) => state.rememberCommentsInQueue);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const rawAccountCommunityAddresses = useModeratedCommunityAddresses();
|
const moderatedCommunityAddressInputs = useModeratedCommunityAddressInputs();
|
||||||
|
const rawAccountCommunityAddresses = useModeratedCommunityAddressesForInputs(moderatedCommunityAddressInputs);
|
||||||
const accountCommunityAddressesKey = getAddressListKey(rawAccountCommunityAddresses);
|
const accountCommunityAddressesKey = getAddressListKey(rawAccountCommunityAddresses);
|
||||||
const accountCommunityAddresses = useMemo(() => getAddressListFromKey(accountCommunityAddressesKey), [accountCommunityAddressesKey]);
|
const accountCommunityAddresses = useMemo(() => getAddressListFromKey(accountCommunityAddressesKey), [accountCommunityAddressesKey]);
|
||||||
|
|
||||||
@@ -1329,28 +1342,31 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp
|
|||||||
const visibleVirtuosoFooterContext = isQueueEmpty ? null : virtuosoFooterContext;
|
const visibleVirtuosoFooterContext = isQueueEmpty ? null : virtuosoFooterContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModQueueContent
|
<>
|
||||||
accountCommunityAddresses={accountCommunityAddresses}
|
<ModQueueCommunityMetadataLoader candidateCommunityAddresses={moderatedCommunityAddressInputs.candidateCommunityAddresses} />
|
||||||
addressToPathMap={addressToPathMap}
|
<ModQueueContent
|
||||||
boardSummaryFeed={boardSummaryFeed}
|
accountCommunityAddresses={accountCommunityAddresses}
|
||||||
compactCardItemContent={compactCardItemContent}
|
addressToPathMap={addressToPathMap}
|
||||||
compactRowItemContent={compactRowItemContent}
|
boardSummaryFeed={boardSummaryFeed}
|
||||||
communityError={visibleCommunityError}
|
compactCardItemContent={compactCardItemContent}
|
||||||
directories={directories}
|
compactRowItemContent={compactRowItemContent}
|
||||||
feedLength={visibleFeedLength}
|
communityError={visibleCommunityError}
|
||||||
feedPostItemContent={feedPostItemContent}
|
directories={directories}
|
||||||
filteredFeed={visibleFilteredFeed}
|
feedLength={visibleFeedLength}
|
||||||
hasMore={visibleHasMore}
|
feedPostItemContent={feedPostItemContent}
|
||||||
isMobile={isMobile}
|
filteredFeed={visibleFilteredFeed}
|
||||||
isQueueEmpty={isQueueEmpty}
|
hasMore={visibleHasMore}
|
||||||
loadMore={visibleLoadMore}
|
isMobile={isMobile}
|
||||||
resolvedAddress={resolvedAddress}
|
isQueueEmpty={isQueueEmpty}
|
||||||
selectedBoardFilter={selectedBoardFilter}
|
loadMore={visibleLoadMore}
|
||||||
setSelectedBoardFilter={setSelectedBoardFilter}
|
resolvedAddress={resolvedAddress}
|
||||||
showBoardColumn={showBoardColumn}
|
selectedBoardFilter={selectedBoardFilter}
|
||||||
viewMode={viewMode}
|
setSelectedBoardFilter={setSelectedBoardFilter}
|
||||||
virtuosoFooterContext={visibleVirtuosoFooterContext}
|
showBoardColumn={showBoardColumn}
|
||||||
/>
|
viewMode={viewMode}
|
||||||
|
virtuosoFooterContext={visibleVirtuosoFooterContext}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user