mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(mod queue): dedupe board filters
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useAccount, useCommunities } from '@bitsocial/bitsocial-react-hooks';
|
||||||
|
import { useDirectories } from './use-directories';
|
||||||
|
import { useCommunityIdentifiers } from './use-community-identifiers';
|
||||||
|
import { useAccountCommunityAddresses } from './use-account-community-addresses';
|
||||||
|
import { getModeratedCommunityAddresses } from '../lib/utils/mod-queue-utils';
|
||||||
|
|
||||||
|
const addUniqueAddress = (addresses: string[], address: string | undefined) => {
|
||||||
|
if (address && !addresses.includes(address)) {
|
||||||
|
addresses.push(address);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useModeratedCommunityAddresses = (): string[] => {
|
||||||
|
const account = useAccount();
|
||||||
|
const accountAddress = account?.author?.address;
|
||||||
|
const accountCommunityAddresses = useAccountCommunityAddresses();
|
||||||
|
const directories = useDirectories();
|
||||||
|
|
||||||
|
const candidateCommunityAddresses = useMemo(() => {
|
||||||
|
const addresses: string[] = [];
|
||||||
|
for (const address of accountCommunityAddresses) {
|
||||||
|
addUniqueAddress(addresses, address);
|
||||||
|
}
|
||||||
|
for (const directory of directories) {
|
||||||
|
addUniqueAddress(addresses, directory.address);
|
||||||
|
}
|
||||||
|
for (const address of account?.subscriptions ?? []) {
|
||||||
|
addUniqueAddress(addresses, address);
|
||||||
|
}
|
||||||
|
return addresses;
|
||||||
|
}, [account?.subscriptions, accountCommunityAddresses, directories]);
|
||||||
|
|
||||||
|
const candidateCommunities = useCommunityIdentifiers(candidateCommunityAddresses);
|
||||||
|
const { communities } = useCommunities(candidateCommunities.length > 0 ? { communities: candidateCommunities } : undefined);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
getModeratedCommunityAddresses({
|
||||||
|
accountAddress,
|
||||||
|
accountCommunityAddresses,
|
||||||
|
candidateCommunityAddresses,
|
||||||
|
communities,
|
||||||
|
}),
|
||||||
|
[accountAddress, accountCommunityAddresses, candidateCommunityAddresses, communities],
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,13 +1,22 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import {
|
import {
|
||||||
filterVisibleModQueueFeed,
|
filterVisibleModQueueFeed,
|
||||||
|
getModQueueBoardFilterGroups,
|
||||||
getModQueueCommentRoute,
|
getModQueueCommentRoute,
|
||||||
|
getModQueueSelectedBoardAddresses,
|
||||||
|
getModeratedCommunityAddresses,
|
||||||
getQueuedCommentRouteState,
|
getQueuedCommentRouteState,
|
||||||
getVisibleQueuedCommentHistory,
|
getVisibleQueuedCommentHistory,
|
||||||
shouldKeepQueuedCommentHistory,
|
shouldKeepQueuedCommentHistory,
|
||||||
} from '../mod-queue-utils';
|
} from '../mod-queue-utils';
|
||||||
|
|
||||||
describe('mod queue utils', () => {
|
describe('mod queue utils', () => {
|
||||||
|
const directories = [
|
||||||
|
{ address: 'anime-primary.bso', publicKey: 'a-primary', directoryCode: 'a', title: '/a/ - Anime & Manga' },
|
||||||
|
{ address: 'anime-backup.bso', publicKey: 'a-backup', directoryCode: 'a', title: '/a/ - Anime & Manga' },
|
||||||
|
{ address: 'tech-primary.bso', publicKey: 'g-primary', directoryCode: 'g', title: '/g/ - Technology' },
|
||||||
|
];
|
||||||
|
|
||||||
it('keeps all queue comments unless they were locally dismissed', () => {
|
it('keeps all queue comments unless they were locally dismissed', () => {
|
||||||
const feed = [
|
const feed = [
|
||||||
{ cid: 'pending', communityAddress: 'tech.eth', pendingApproval: true },
|
{ cid: 'pending', communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
@@ -37,6 +46,57 @@ describe('mod queue utils', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('applies grouped board filters to all directory aliases', () => {
|
||||||
|
const feed = [
|
||||||
|
{ cid: 'a-primary-pending', communityAddress: 'a-primary', pendingApproval: true },
|
||||||
|
{ cid: 'a-backup-pending', communityAddress: 'a-backup', pendingApproval: true },
|
||||||
|
{ cid: 'g-pending', communityAddress: 'g-primary', pendingApproval: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(filterVisibleModQueueFeed(feed, 'a', new Set(['a-primary-pending']), ['a-primary', 'a-backup'])).toEqual([
|
||||||
|
{ cid: 'a-backup-pending', communityAddress: 'a-backup', pendingApproval: true },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dedupes mod queue board filters by directory path', () => {
|
||||||
|
expect(getModQueueBoardFilterGroups(['a-backup', 'custom.eth', 'g-primary', 'a-primary'], directories, [['g', 'a']])).toEqual([
|
||||||
|
{
|
||||||
|
addresses: ['g-primary'],
|
||||||
|
boardPath: 'g',
|
||||||
|
filterKey: 'g',
|
||||||
|
isDirectory: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addresses: ['a-backup', 'a-primary'],
|
||||||
|
boardPath: 'a',
|
||||||
|
filterKey: 'a',
|
||||||
|
isDirectory: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addresses: ['custom.eth'],
|
||||||
|
boardPath: 'custom.eth',
|
||||||
|
filterKey: 'custom.eth',
|
||||||
|
isDirectory: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expands selected directory filters to every matching moderated address', () => {
|
||||||
|
expect(getModQueueSelectedBoardAddresses(['a-primary', 'g-primary', 'a-backup'], 'a', directories)).toEqual(['a-primary', 'a-backup']);
|
||||||
|
expect(getModQueueSelectedBoardAddresses(['a-primary', 'g-primary', 'a-backup'], 'a-primary', directories)).toEqual(['a-primary', 'a-backup']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds live role communities that are not cached on the account', () => {
|
||||||
|
expect(
|
||||||
|
getModeratedCommunityAddresses({
|
||||||
|
accountAddress: 'plebeius.bso',
|
||||||
|
accountCommunityAddresses: ['tech-primary.bso'],
|
||||||
|
candidateCommunityAddresses: ['tech-primary.bso', 'paranormal-posting.bso', 'viewer-board.bso'],
|
||||||
|
communities: [undefined, { roles: { 'plebeius.bso': { role: 'moderator' } } }, { roles: { 'plebeius.bso': { role: 'viewer' } } }],
|
||||||
|
}),
|
||||||
|
).toEqual(['tech-primary.bso', 'paranormal-posting.bso']);
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps only terminal local moderation states in queue history', () => {
|
it('keeps only terminal local moderation states in queue history', () => {
|
||||||
expect(shouldKeepQueuedCommentHistory({ cid: 'pending', pendingApproval: true })).toBe(false);
|
expect(shouldKeepQueuedCommentHistory({ cid: 'pending', pendingApproval: true })).toBe(false);
|
||||||
expect(shouldKeepQueuedCommentHistory({ cid: 'published', pendingApproval: false })).toBe(false);
|
expect(shouldKeepQueuedCommentHistory({ cid: 'published', pendingApproval: false })).toBe(false);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import type { Comment } from '@bitsocial/bitsocial-react-hooks';
|
import type { Comment } from '@bitsocial/bitsocial-react-hooks';
|
||||||
|
import type { DirectoryCommunity } from '../../hooks/use-directories';
|
||||||
import { getCommentCommunityAddress } from './comment-utils';
|
import { getCommentCommunityAddress } from './comment-utils';
|
||||||
|
import { hasModQueueAccessRole } from './mod-access';
|
||||||
import { isPendingApprovalRejected } from './pending-approval-moderation';
|
import { isPendingApprovalRejected } from './pending-approval-moderation';
|
||||||
import { areSameBoardAddress } from './route-utils';
|
import { areSameBoardAddress, getBoardPath } from './route-utils';
|
||||||
import { getThreadTopNavigationState } from './thread-scroll-utils';
|
import { getThreadTopNavigationState } from './thread-scroll-utils';
|
||||||
|
|
||||||
type ModQueueCommentLike = {
|
type ModQueueCommentLike = {
|
||||||
@@ -59,9 +61,130 @@ export type QueuedCommentSnapshot = {
|
|||||||
title?: Comment['title'];
|
title?: Comment['title'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface ModQueueBoardFilterGroup {
|
||||||
|
addresses: string[];
|
||||||
|
boardPath: string;
|
||||||
|
filterKey: string;
|
||||||
|
isDirectory: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModQueueCommunityRoleSource {
|
||||||
|
roles?: Record<string, { role?: string } | undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
export const getModQueueCommentRoute = (boardPath: string | undefined, commentCid: string | undefined): string | undefined =>
|
export const getModQueueCommentRoute = (boardPath: string | undefined, commentCid: string | undefined): string | undefined =>
|
||||||
boardPath && commentCid ? `/${boardPath}/thread/${commentCid}` : undefined;
|
boardPath && commentCid ? `/${boardPath}/thread/${commentCid}` : undefined;
|
||||||
|
|
||||||
|
export const getModQueueBoardFilterKey = (communityAddress: string, directories: DirectoryCommunity[]): string => {
|
||||||
|
const boardPath = getBoardPath(communityAddress, directories);
|
||||||
|
return boardPath !== communityAddress ? boardPath : communityAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addUniqueBoardAddress = (addresses: string[], address: string) => {
|
||||||
|
if (!addresses.some((existingAddress) => areSameBoardAddress(existingAddress, address))) {
|
||||||
|
addresses.push(address);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getModQueueBoardFilterGroups = (
|
||||||
|
accountCommunityAddresses: readonly string[],
|
||||||
|
directories: DirectoryCommunity[],
|
||||||
|
boardCodeGroups: readonly (readonly string[])[],
|
||||||
|
): ModQueueBoardFilterGroup[] => {
|
||||||
|
const groupsByFilterKey = new Map<string, ModQueueBoardFilterGroup>();
|
||||||
|
const filterKeyByAddress = new Map<string, string>();
|
||||||
|
|
||||||
|
for (const address of accountCommunityAddresses) {
|
||||||
|
const boardPath = getBoardPath(address, directories);
|
||||||
|
const filterKey = boardPath !== address ? boardPath : address;
|
||||||
|
const existingGroup = groupsByFilterKey.get(filterKey);
|
||||||
|
if (existingGroup) {
|
||||||
|
addUniqueBoardAddress(existingGroup.addresses, address);
|
||||||
|
} else {
|
||||||
|
groupsByFilterKey.set(filterKey, {
|
||||||
|
addresses: [address],
|
||||||
|
boardPath,
|
||||||
|
filterKey,
|
||||||
|
isDirectory: boardPath !== address,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
filterKeyByAddress.set(address, filterKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderedGroups: ModQueueBoardFilterGroup[] = [];
|
||||||
|
const seenFilterKeys = new Set<string>();
|
||||||
|
const addGroup = (filterKey: string | undefined) => {
|
||||||
|
if (!filterKey || seenFilterKeys.has(filterKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const group = groupsByFilterKey.get(filterKey);
|
||||||
|
if (!group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
orderedGroups.push(group);
|
||||||
|
seenFilterKeys.add(filterKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const group of boardCodeGroups) {
|
||||||
|
for (const code of group) {
|
||||||
|
addGroup(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const address of accountCommunityAddresses) {
|
||||||
|
addGroup(filterKeyByAddress.get(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedGroups;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getModQueueSelectedBoardAddresses = (
|
||||||
|
communityAddresses: readonly string[],
|
||||||
|
selectedBoardFilter: string | null,
|
||||||
|
directories: DirectoryCommunity[],
|
||||||
|
): string[] | null => {
|
||||||
|
if (!selectedBoardFilter) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedFilterKey = getModQueueBoardFilterKey(selectedBoardFilter, directories);
|
||||||
|
const selectedAddresses = communityAddresses.filter((address) => {
|
||||||
|
if (areSameBoardAddress(address, selectedBoardFilter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return getModQueueBoardFilterKey(address, directories) === selectedFilterKey;
|
||||||
|
});
|
||||||
|
|
||||||
|
return selectedAddresses.length > 0 ? selectedAddresses : [selectedBoardFilter];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getModeratedCommunityAddresses = ({
|
||||||
|
accountAddress,
|
||||||
|
accountCommunityAddresses,
|
||||||
|
candidateCommunityAddresses,
|
||||||
|
communities,
|
||||||
|
}: {
|
||||||
|
accountAddress: string | undefined;
|
||||||
|
accountCommunityAddresses: readonly string[];
|
||||||
|
candidateCommunityAddresses: readonly string[];
|
||||||
|
communities: readonly (ModQueueCommunityRoleSource | undefined)[];
|
||||||
|
}): string[] => {
|
||||||
|
const moderatedAddresses = [...accountCommunityAddresses];
|
||||||
|
|
||||||
|
if (!accountAddress) {
|
||||||
|
return moderatedAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidateCommunityAddresses.forEach((candidateAddress, index) => {
|
||||||
|
const role = communities[index]?.roles?.[accountAddress]?.role;
|
||||||
|
if (hasModQueueAccessRole(role)) {
|
||||||
|
addUniqueBoardAddress(moderatedAddresses, candidateAddress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return moderatedAddresses;
|
||||||
|
};
|
||||||
|
|
||||||
export const getQueuedCommentSnapshot = (comment: ModQueueCommentLike | undefined): QueuedCommentSnapshot | undefined => {
|
export const getQueuedCommentSnapshot = (comment: ModQueueCommentLike | undefined): QueuedCommentSnapshot | undefined => {
|
||||||
if (!comment?.cid) {
|
if (!comment?.cid) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -129,6 +252,7 @@ export const filterVisibleModQueueFeed = <T extends ModQueueCommentLike>(
|
|||||||
feed: T[],
|
feed: T[],
|
||||||
selectedBoardFilter: string | null,
|
selectedBoardFilter: string | null,
|
||||||
dismissedCommentCids: ReadonlySet<string> = emptyDismissedCommentCids,
|
dismissedCommentCids: ReadonlySet<string> = emptyDismissedCommentCids,
|
||||||
|
selectedBoardFilterAddresses: readonly string[] | null = null,
|
||||||
): T[] =>
|
): T[] =>
|
||||||
feed.filter((comment) => {
|
feed.filter((comment) => {
|
||||||
if (comment.cid && dismissedCommentCids.has(comment.cid)) {
|
if (comment.cid && dismissedCommentCids.has(comment.cid)) {
|
||||||
@@ -139,5 +263,14 @@ export const filterVisibleModQueueFeed = <T extends ModQueueCommentLike>(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getCommentCommunityAddress(comment) === selectedBoardFilter;
|
const commentCommunityAddress = getCommentCommunityAddress(comment);
|
||||||
|
if (!commentCommunityAddress) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedBoardFilterAddresses?.length) {
|
||||||
|
return selectedBoardFilterAddresses.some((address) => areSameBoardAddress(address, commentCommunityAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
return areSameBoardAddress(commentCommunityAddress, selectedBoardFilter);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import useModQueueStore from '../../stores/use-mod-queue-store';
|
|||||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||||
import ErrorDisplay from '../../components/error-display/error-display';
|
import ErrorDisplay from '../../components/error-display/error-display';
|
||||||
import { useFeedStateString } from '../../hooks/use-state-string';
|
import { useFeedStateString } from '../../hooks/use-state-string';
|
||||||
import { getCommunityAddress, getBoardPath, extractDirectoryFromTitle, areSameBoardAddress } from '../../lib/utils/route-utils';
|
import { getCommunityAddress, getBoardPath, areSameBoardAddress } from '../../lib/utils/route-utils';
|
||||||
import { useDirectories, DirectoryCommunity } from '../../hooks/use-directories';
|
import { useDirectories, DirectoryCommunity } from '../../hooks/use-directories';
|
||||||
import getShortAddress from '../../lib/get-short-address';
|
import getShortAddress from '../../lib/get-short-address';
|
||||||
import { BOARD_CODE_GROUPS } from '../../constants/board-codes';
|
import { BOARD_CODE_GROUPS } from '../../constants/board-codes';
|
||||||
@@ -28,14 +28,16 @@ import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
|||||||
import { formatErrorForDisplay } from '../../lib/utils/error-utils';
|
import { formatErrorForDisplay } from '../../lib/utils/error-utils';
|
||||||
import {
|
import {
|
||||||
filterVisibleModQueueFeed,
|
filterVisibleModQueueFeed,
|
||||||
|
getModQueueBoardFilterGroups,
|
||||||
|
getModQueueBoardFilterKey,
|
||||||
getModQueueCommentRoute,
|
getModQueueCommentRoute,
|
||||||
|
getModQueueSelectedBoardAddresses,
|
||||||
getQueuedCommentRouteState,
|
getQueuedCommentRouteState,
|
||||||
getQueuedCommentSnapshot,
|
getQueuedCommentSnapshot,
|
||||||
getVisibleQueuedCommentHistory,
|
getVisibleQueuedCommentHistory,
|
||||||
shouldKeepQueuedCommentHistory,
|
shouldKeepQueuedCommentHistory,
|
||||||
} from '../../lib/utils/mod-queue-utils';
|
} from '../../lib/utils/mod-queue-utils';
|
||||||
import Tooltip from '../../components/tooltip';
|
import Tooltip from '../../components/tooltip';
|
||||||
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
|
|
||||||
import { useCommunityIdentifier, useCommunityIdentifiers } from '../../hooks/use-community-identifiers';
|
import { useCommunityIdentifier, useCommunityIdentifiers } from '../../hooks/use-community-identifiers';
|
||||||
import useIsMobile from '../../hooks/use-is-mobile';
|
import useIsMobile from '../../hooks/use-is-mobile';
|
||||||
import { useCurrentTime } from '../../hooks/use-current-time';
|
import { useCurrentTime } from '../../hooks/use-current-time';
|
||||||
@@ -45,6 +47,7 @@ import capitalize from 'lodash/capitalize';
|
|||||||
import lowerCase from 'lodash/lowerCase';
|
import lowerCase from 'lodash/lowerCase';
|
||||||
import { PageFooterDesktop, PageFooterMobile, StyleOnlyFooterFirstRow } from '../../components/footer';
|
import { PageFooterDesktop, PageFooterMobile, StyleOnlyFooterFirstRow } from '../../components/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';
|
||||||
|
|
||||||
const { addChallenge } = useChallengesStore.getState();
|
const { addChallenge } = useChallengesStore.getState();
|
||||||
|
|
||||||
@@ -590,15 +593,6 @@ interface ModQueueBoardSummaryProps {
|
|||||||
accountCommunityAddresses: string[];
|
accountCommunityAddresses: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const findBoardAddressByCode = (code: string, dirs: DirectoryCommunity[]): string | null => {
|
|
||||||
const entry = dirs.find((sub) => {
|
|
||||||
if (!sub.title) return false;
|
|
||||||
const directory = extractDirectoryFromTitle(sub.title);
|
|
||||||
return directory === code;
|
|
||||||
});
|
|
||||||
return entry?.address || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ModQueueBoardCount = ({ normal, urgent }: { normal: number; urgent: number }) => {
|
const ModQueueBoardCount = ({ normal, urgent }: { normal: number; urgent: number }) => {
|
||||||
const total = normal + urgent;
|
const total = normal + urgent;
|
||||||
if (total === 0) return null;
|
if (total === 0) return null;
|
||||||
@@ -627,18 +621,22 @@ const ModQueueBoardSummary = ({ feed, directories, accountCommunityAddresses }:
|
|||||||
const getAlertThresholdSeconds = useModQueueStore((state) => state.getAlertThresholdSeconds);
|
const getAlertThresholdSeconds = useModQueueStore((state) => state.getAlertThresholdSeconds);
|
||||||
const currentTime = useCurrentTime();
|
const currentTime = useCurrentTime();
|
||||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||||
const modAddressSet = useMemo(() => new Set(accountCommunityAddresses), [accountCommunityAddresses]);
|
|
||||||
const locallyModeratedFeed = useLocallyModeratedModQueueFeed(feed, currentTime);
|
const locallyModeratedFeed = useLocallyModeratedModQueueFeed(feed, currentTime);
|
||||||
|
const boardGroups = useMemo(() => getModQueueBoardFilterGroups(accountCommunityAddresses, directories, BOARD_CODE_GROUPS), [accountCommunityAddresses, directories]);
|
||||||
|
const selectedBoardFilterAddresses = useMemo(
|
||||||
|
() => getModQueueSelectedBoardAddresses(accountCommunityAddresses, selectedBoardFilter, directories),
|
||||||
|
[accountCommunityAddresses, selectedBoardFilter, directories],
|
||||||
|
);
|
||||||
|
|
||||||
const boardCounts = useMemo(() => {
|
const boardCounts = useMemo(() => {
|
||||||
const counts = new Map<string, { normal: number; urgent: number }>();
|
const counts = new Map<string, { normal: number; urgent: number }>();
|
||||||
for (const address of accountCommunityAddresses) {
|
for (const group of boardGroups) {
|
||||||
counts.set(address, { normal: 0, urgent: 0 });
|
counts.set(group.filterKey, { normal: 0, urgent: 0 });
|
||||||
}
|
}
|
||||||
for (const item of locallyModeratedFeed) {
|
for (const item of locallyModeratedFeed) {
|
||||||
const addr = getCommentCommunityAddress(item);
|
const addr = getCommentCommunityAddress(item);
|
||||||
if (!addr) continue;
|
if (!addr) continue;
|
||||||
const entry = counts.get(addr);
|
const entry = counts.get(getModQueueBoardFilterKey(addr, directories));
|
||||||
if (!entry) continue;
|
if (!entry) continue;
|
||||||
const isAwaiting = isPendingApprovalAwaiting(item);
|
const isAwaiting = isPendingApprovalAwaiting(item);
|
||||||
if (!isAwaiting) continue;
|
if (!isAwaiting) continue;
|
||||||
@@ -648,7 +646,7 @@ const ModQueueBoardSummary = ({ feed, directories, accountCommunityAddresses }:
|
|||||||
else entry.normal++;
|
else entry.normal++;
|
||||||
}
|
}
|
||||||
return counts;
|
return counts;
|
||||||
}, [locallyModeratedFeed, accountCommunityAddresses, currentTime, alertThresholdSeconds]);
|
}, [locallyModeratedFeed, boardGroups, currentTime, alertThresholdSeconds, directories]);
|
||||||
|
|
||||||
const { totalNormal, totalUrgent } = useMemo(() => {
|
const { totalNormal, totalUrgent } = useMemo(() => {
|
||||||
let normal = 0;
|
let normal = 0;
|
||||||
@@ -660,41 +658,10 @@ const ModQueueBoardSummary = ({ feed, directories, accountCommunityAddresses }:
|
|||||||
return { totalNormal: normal, totalUrgent: urgent };
|
return { totalNormal: normal, totalUrgent: urgent };
|
||||||
}, [boardCounts]);
|
}, [boardCounts]);
|
||||||
|
|
||||||
// Order: All first, then BOARD_CODE_GROUPS order (directory boards), then non-directory boards
|
|
||||||
const orderedAddresses = useMemo(() => {
|
|
||||||
const ordered: string[] = [];
|
|
||||||
const seen = new Set<string>();
|
|
||||||
|
|
||||||
for (const group of BOARD_CODE_GROUPS) {
|
|
||||||
for (const code of group) {
|
|
||||||
const address = findBoardAddressByCode(code, directories);
|
|
||||||
if (address && modAddressSet.has(address) && !seen.has(address)) {
|
|
||||||
ordered.push(address);
|
|
||||||
seen.add(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Directory boards not in BOARD_CODE_GROUPS (custom dirs)
|
|
||||||
for (const addr of accountCommunityAddresses) {
|
|
||||||
const path = getBoardPath(addr, directories);
|
|
||||||
if (path !== addr && !seen.has(addr)) {
|
|
||||||
ordered.push(addr);
|
|
||||||
seen.add(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Non-directory boards (own category, like subscriptions in boardsbar)
|
|
||||||
for (const addr of accountCommunityAddresses) {
|
|
||||||
if (!seen.has(addr)) {
|
|
||||||
ordered.push(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ordered;
|
|
||||||
}, [accountCommunityAddresses, directories, modAddressSet]);
|
|
||||||
|
|
||||||
const handleSelectAll = useCallback(() => setSelectedBoardFilter(null), [setSelectedBoardFilter]);
|
const handleSelectAll = useCallback(() => setSelectedBoardFilter(null), [setSelectedBoardFilter]);
|
||||||
const handleSelectBoard = useCallback((address: string) => setSelectedBoardFilter(address), [setSelectedBoardFilter]);
|
const handleSelectBoard = useCallback((filterKey: string) => setSelectedBoardFilter(filterKey), [setSelectedBoardFilter]);
|
||||||
|
|
||||||
if (accountCommunityAddresses.length === 0) {
|
if (boardGroups.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,20 +676,23 @@ const ModQueueBoardSummary = ({ feed, directories, accountCommunityAddresses }:
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{orderedAddresses.map((address) => {
|
{boardGroups.map((group) => {
|
||||||
const boardPath = getBoardPath(address, directories);
|
const displayText =
|
||||||
const isInDirectory = boardPath !== address;
|
group.isDirectory || group.filterKey.endsWith('.eth') || group.filterKey.endsWith('.sol')
|
||||||
const displayText = isInDirectory ? boardPath : address.endsWith('.eth') || address.endsWith('.sol') ? address : getShortAddress(address) || address;
|
? group.filterKey
|
||||||
const isSelected = selectedBoardFilter === address;
|
: getShortAddress(group.filterKey) || group.filterKey;
|
||||||
const { normal, urgent } = boardCounts.get(address) ?? { normal: 0, urgent: 0 };
|
const isSelected =
|
||||||
|
selectedBoardFilter === group.filterKey ||
|
||||||
|
group.addresses.some((address) => selectedBoardFilterAddresses?.some((selectedAddress) => areSameBoardAddress(address, selectedAddress)));
|
||||||
|
const { normal, urgent } = boardCounts.get(group.filterKey) ?? { normal: 0, urgent: 0 };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={address}>
|
<React.Fragment key={group.filterKey}>
|
||||||
{' / '}
|
{' / '}
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={`${styles.boardSummaryLink} ${isSelected ? styles.boardSummaryLinkSelected : ''}`}
|
className={`${styles.boardSummaryLink} ${isSelected ? styles.boardSummaryLinkSelected : ''}`}
|
||||||
onClick={() => handleSelectBoard(address)}
|
onClick={() => handleSelectBoard(group.filterKey)}
|
||||||
>
|
>
|
||||||
{displayText}
|
{displayText}
|
||||||
{normal + urgent > 0 && (
|
{normal + urgent > 0 && (
|
||||||
@@ -804,7 +774,7 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp
|
|||||||
|
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const accountAddress = account?.author?.address;
|
const accountAddress = account?.author?.address;
|
||||||
const accountCommunityAddresses = useAccountCommunityAddresses();
|
const accountCommunityAddresses = useModeratedCommunityAddresses();
|
||||||
|
|
||||||
const directories = useDirectories();
|
const directories = useDirectories();
|
||||||
|
|
||||||
@@ -877,7 +847,7 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp
|
|||||||
const rememberCommentsInQueue = useModQueueStore((state) => state.rememberCommentsInQueue);
|
const rememberCommentsInQueue = useModQueueStore((state) => state.rememberCommentsInQueue);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const accountCommunityAddresses = useAccountCommunityAddresses();
|
const accountCommunityAddresses = useModeratedCommunityAddresses();
|
||||||
|
|
||||||
const directories = useDirectories();
|
const directories = useDirectories();
|
||||||
|
|
||||||
@@ -931,9 +901,13 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp
|
|||||||
);
|
);
|
||||||
|
|
||||||
const dismissedCommentCidSet = useMemo(() => new Set(dismissedCommentCids), [dismissedCommentCids]);
|
const dismissedCommentCidSet = useMemo(() => new Set(dismissedCommentCids), [dismissedCommentCids]);
|
||||||
|
const selectedBoardFilterAddresses = useMemo(
|
||||||
|
() => getModQueueSelectedBoardAddresses(communityAddresses, selectedBoardFilter, directories),
|
||||||
|
[communityAddresses, selectedBoardFilter, directories],
|
||||||
|
);
|
||||||
const filteredFeed = useMemo(
|
const filteredFeed = useMemo(
|
||||||
() => filterVisibleModQueueFeed(feedWithHistory, selectedBoardFilter, dismissedCommentCidSet),
|
() => filterVisibleModQueueFeed(feedWithHistory, selectedBoardFilter, dismissedCommentCidSet, selectedBoardFilterAddresses),
|
||||||
[feedWithHistory, selectedBoardFilter, dismissedCommentCidSet],
|
[feedWithHistory, selectedBoardFilter, dismissedCommentCidSet, selectedBoardFilterAddresses],
|
||||||
);
|
);
|
||||||
|
|
||||||
const addressToPathMap = useMemo(() => {
|
const addressToPathMap = useMemo(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user