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:
@@ -1,13 +1,22 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
filterVisibleModQueueFeed,
|
||||
getModQueueBoardFilterGroups,
|
||||
getModQueueCommentRoute,
|
||||
getModQueueSelectedBoardAddresses,
|
||||
getModeratedCommunityAddresses,
|
||||
getQueuedCommentRouteState,
|
||||
getVisibleQueuedCommentHistory,
|
||||
shouldKeepQueuedCommentHistory,
|
||||
} from '../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', () => {
|
||||
const feed = [
|
||||
{ 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', () => {
|
||||
expect(shouldKeepQueuedCommentHistory({ cid: 'pending', pendingApproval: true })).toBe(false);
|
||||
expect(shouldKeepQueuedCommentHistory({ cid: 'published', pendingApproval: false })).toBe(false);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { Comment } from '@bitsocial/bitsocial-react-hooks';
|
||||
import type { DirectoryCommunity } from '../../hooks/use-directories';
|
||||
import { getCommentCommunityAddress } from './comment-utils';
|
||||
import { hasModQueueAccessRole } from './mod-access';
|
||||
import { isPendingApprovalRejected } from './pending-approval-moderation';
|
||||
import { areSameBoardAddress } from './route-utils';
|
||||
import { areSameBoardAddress, getBoardPath } from './route-utils';
|
||||
import { getThreadTopNavigationState } from './thread-scroll-utils';
|
||||
|
||||
type ModQueueCommentLike = {
|
||||
@@ -59,9 +61,130 @@ export type QueuedCommentSnapshot = {
|
||||
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 =>
|
||||
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 => {
|
||||
if (!comment?.cid) {
|
||||
return undefined;
|
||||
@@ -129,6 +252,7 @@ export const filterVisibleModQueueFeed = <T extends ModQueueCommentLike>(
|
||||
feed: T[],
|
||||
selectedBoardFilter: string | null,
|
||||
dismissedCommentCids: ReadonlySet<string> = emptyDismissedCommentCids,
|
||||
selectedBoardFilterAddresses: readonly string[] | null = null,
|
||||
): T[] =>
|
||||
feed.filter((comment) => {
|
||||
if (comment.cid && dismissedCommentCids.has(comment.cid)) {
|
||||
@@ -139,5 +263,14 @@ export const filterVisibleModQueueFeed = <T extends ModQueueCommentLike>(
|
||||
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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user