mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
refactor(core): remove legacy plebbit terminology
This commit is contained in:
@@ -9,9 +9,7 @@ describe('buildEditableAccountJson', () => {
|
||||
author: { address: '0x123', shortAddress: '0x1...3', avatar: { url: 'https://example.com' } },
|
||||
pkc: { someOption: true },
|
||||
pkcReactOptions: { foo: 'baz' },
|
||||
plebbit: { someOption: true },
|
||||
karma: 42,
|
||||
plebbitReactOptions: { foo: 'bar' },
|
||||
unreadNotificationCount: 5,
|
||||
};
|
||||
const result = JSON.parse(buildEditableAccountJson(account));
|
||||
@@ -21,9 +19,7 @@ describe('buildEditableAccountJson', () => {
|
||||
expect(result.account.author.avatar).toBeUndefined();
|
||||
expect(result.account.pkc).toBeUndefined();
|
||||
expect(result.account.pkcReactOptions).toBeUndefined();
|
||||
expect(result.account.plebbit).toBeUndefined();
|
||||
expect(result.account.karma).toBeUndefined();
|
||||
expect(result.account.plebbitReactOptions).toBeUndefined();
|
||||
expect(result.account.unreadNotificationCount).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ describe('challenge-utils', () => {
|
||||
challengeSuccess: false,
|
||||
reason: 'try again later',
|
||||
} as never,
|
||||
{ subplebbitAddress: 'business-and-finance.bso' },
|
||||
{ communityAddress: 'business-and-finance.bso' },
|
||||
);
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'Challenge Verification Failed:',
|
||||
expect.objectContaining({ challengeSuccess: false }),
|
||||
'Publication:',
|
||||
expect.objectContaining({ subplebbitAddress: 'business-and-finance.bso' }),
|
||||
expect.objectContaining({ communityAddress: 'business-and-finance.bso' }),
|
||||
);
|
||||
expect(alertMock).toHaveBeenCalledWith('Error from /biz/: invalid captcha try again later');
|
||||
});
|
||||
@@ -44,13 +44,13 @@ describe('challenge-utils', () => {
|
||||
challengeErrors: ['first error', 'second error'],
|
||||
challengeSuccess: false,
|
||||
} as never,
|
||||
{ subplebbitAddress: 'unknown-board.eth' },
|
||||
{ communityAddress: 'unknown-board.eth' },
|
||||
);
|
||||
|
||||
expect(alertMock).toHaveBeenCalledWith('Error from unknown-board.eth: first error second error');
|
||||
});
|
||||
|
||||
it('uses communityAddress when the upgraded publication no longer exposes subplebbitAddress', () => {
|
||||
it('uses communityAddress when the upgraded publication no longer exposes communityAddress', () => {
|
||||
alertChallengeVerificationFailed(
|
||||
{
|
||||
challengeErrors: ['first error'],
|
||||
@@ -76,7 +76,7 @@ describe('challenge-utils', () => {
|
||||
});
|
||||
|
||||
it('logs successful challenge verification instead of alerting', () => {
|
||||
alertChallengeVerificationFailed({ challengeSuccess: true } as never, { subplebbitAddress: 'business-and-finance.bso' });
|
||||
alertChallengeVerificationFailed({ challengeSuccess: true } as never, { communityAddress: 'business-and-finance.bso' });
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith('Challenge verification succeeded:', expect.objectContaining({ challengeSuccess: true }));
|
||||
expect(alertMock).not.toHaveBeenCalled();
|
||||
|
||||
@@ -72,18 +72,18 @@ describe('pattern-utils', () => {
|
||||
it('matches roles with moderator aliases and rejects missing role metadata', () => {
|
||||
const modComment = {
|
||||
author: { address: 'author-1' },
|
||||
subplebbitAddress: 'music-posting.eth',
|
||||
communityAddress: 'music-posting.eth',
|
||||
};
|
||||
const ownerComment = {
|
||||
author: { address: 'author-2' },
|
||||
subplebbitAddress: 'music-posting.eth',
|
||||
communityAddress: 'music-posting.eth',
|
||||
};
|
||||
|
||||
expect(userHasRole(modComment as never, 'moderator')).toBe(true);
|
||||
expect(userHasRole(modComment as never, 'mod')).toBe(true);
|
||||
expect(userHasRole(ownerComment as never, 'owner')).toBe(true);
|
||||
expect(userHasRole(ownerComment as never, 'admin')).toBe(false);
|
||||
expect(userHasRole({ author: { address: 'missing' }, subplebbitAddress: 'unknown.eth' } as never, 'moderator')).toBe(false);
|
||||
expect(userHasRole({ author: { address: 'missing' }, communityAddress: 'unknown.eth' } as never, 'moderator')).toBe(false);
|
||||
});
|
||||
|
||||
it('parses mixed special filters and content filters', () => {
|
||||
@@ -109,7 +109,7 @@ describe('pattern-utils', () => {
|
||||
shortAddress: 'auth1',
|
||||
},
|
||||
content: 'That feel when the girlfriend texts back',
|
||||
subplebbitAddress: 'music-posting.eth',
|
||||
communityAddress: 'music-posting.eth',
|
||||
title: 'TFW',
|
||||
};
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ describe('post-page-resolution', () => {
|
||||
it('only accepts strict board feed options for the active single-board feed', () => {
|
||||
const baseOptions = {
|
||||
sortType: 'active',
|
||||
subplebbitAddresses: ['music.eth'],
|
||||
communityAddresses: ['music.eth'],
|
||||
};
|
||||
|
||||
expect(isBoardFeedOptions(baseOptions, 'music.eth')).toBe(true);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, sortType: 'new' }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, subplebbitAddresses: ['music.eth', 'tech.eth'] }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, subplebbitAddresses: ['tech.eth'] }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, communityAddresses: ['music.eth', 'tech.eth'] }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, communityAddresses: ['tech.eth'] }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, filter: { title: 'test' } }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, newerThan: 3600 }, 'music.eth')).toBe(false);
|
||||
expect(isBoardFeedOptions({ ...baseOptions, modQueue: true }, 'music.eth')).toBe(false);
|
||||
@@ -31,16 +31,16 @@ describe('post-page-resolution', () => {
|
||||
const feedsOptions = {
|
||||
allFeed: {
|
||||
sortType: 'active',
|
||||
subplebbitAddresses: ['all.eth'],
|
||||
communityAddresses: ['all.eth'],
|
||||
},
|
||||
catalogFilterFeed: {
|
||||
filter: { title: 'match' },
|
||||
sortType: 'active',
|
||||
subplebbitAddresses: ['music.eth'],
|
||||
communityAddresses: ['music.eth'],
|
||||
},
|
||||
boardFeed: {
|
||||
sortType: 'active',
|
||||
subplebbitAddresses: ['music.eth'],
|
||||
communityAddresses: ['music.eth'],
|
||||
},
|
||||
};
|
||||
const loadedFeeds = {
|
||||
|
||||
@@ -6,7 +6,7 @@ const createReply = (overrides: Partial<Comment> = {}) =>
|
||||
({
|
||||
cid: 'reply-cid',
|
||||
parentCid: 'target-cid',
|
||||
subplebbitAddress: 'music.eth',
|
||||
communityAddress: 'music.eth',
|
||||
...overrides,
|
||||
}) as Comment;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
getFeedCacheKey,
|
||||
getFeedType,
|
||||
getPageFromFeedPath,
|
||||
getSubplebbitAddress,
|
||||
getCommunityAddress,
|
||||
isArchiveRoute,
|
||||
isBoardModRoute,
|
||||
isDirectoryBoard,
|
||||
@@ -44,9 +44,9 @@ describe('directory mapping helpers', () => {
|
||||
expect(getBoardPath('12D3KooWQdQ6TkVA1Xe9zzaFP6vXBgsLeMAewpLpLwbsAYKivnQy', communities)).toBe('mu');
|
||||
expect(getBoardPath('unknown.example', communities)).toBe('unknown.example');
|
||||
|
||||
expect(getSubplebbitAddress('biz', communities)).toBe('business.eth');
|
||||
expect(getSubplebbitAddress('b', communities)).toBe('random.eth');
|
||||
expect(getSubplebbitAddress('unknown.example', communities)).toBe('unknown.example');
|
||||
expect(getCommunityAddress('biz', communities)).toBe('business.eth');
|
||||
expect(getCommunityAddress('b', communities)).toBe('random.eth');
|
||||
expect(getCommunityAddress('unknown.example', communities)).toBe('unknown.example');
|
||||
});
|
||||
|
||||
it('compares aliases and directory identifiers correctly', () => {
|
||||
|
||||
@@ -38,9 +38,9 @@ describe('view-utils', () => {
|
||||
expect(isSettingsView('/music.eth/thread/cid-123/settings', params)).toBe(true);
|
||||
});
|
||||
|
||||
it('supports deprecated subplebbitAddress params and marks unknown routes as not found', () => {
|
||||
it('supports emoji board identifiers and marks unknown routes as not found', () => {
|
||||
const params = {
|
||||
subplebbitAddress: 'emoji-🎵.eth',
|
||||
boardIdentifier: 'emoji-🎵.eth',
|
||||
commentCid: 'cid-123',
|
||||
};
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ type AccountLike = {
|
||||
author?: { address?: string; shortAddress?: string; avatar?: unknown };
|
||||
pkc?: unknown;
|
||||
pkcReactOptions?: unknown;
|
||||
plebbit?: unknown;
|
||||
karma?: unknown;
|
||||
plebbitReactOptions?: unknown;
|
||||
unreadNotificationCount?: unknown;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
@@ -23,9 +21,7 @@ export const buildEditableAccountJson = (account: AccountLike | undefined): stri
|
||||
author: { ...account?.author, avatar: undefined },
|
||||
pkc: undefined,
|
||||
pkcReactOptions: undefined,
|
||||
plebbit: undefined,
|
||||
karma: undefined,
|
||||
plebbitReactOptions: undefined,
|
||||
unreadNotificationCount: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ChallengeVerification } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { getFallbackDirectoriesData } from '../../hooks/use-directories';
|
||||
import { getCommentCommunityAddress } from './comment-utils';
|
||||
import { getBoardPath } from './route-utils';
|
||||
|
||||
const resolveBoardIdentifier = (communityAddress: unknown): string => {
|
||||
@@ -35,7 +36,7 @@ export const alertChallengeVerificationFailed = (challengeVerification: Challeng
|
||||
}
|
||||
|
||||
const finalMessage = errorMessages.filter(Boolean).join(' ');
|
||||
const publicationCommunityAddress = publication?.communityAddress || publication?.subplebbitAddress;
|
||||
const publicationCommunityAddress = getCommentCommunityAddress(publication);
|
||||
|
||||
alert(`Error from ${resolveBoardIdentifier(publicationCommunityAddress)}: ${finalMessage || 'unknown error'}`);
|
||||
} else {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
type CommentWithLegacyCommunityAddress = {
|
||||
type CommentWithCommunityAddress = {
|
||||
communityAddress?: string;
|
||||
replies?: {
|
||||
pages?: Record<
|
||||
string,
|
||||
| {
|
||||
comments?: Array<CommentWithLegacyCommunityAddress | undefined>;
|
||||
comments?: Array<CommentWithCommunityAddress | undefined>;
|
||||
}
|
||||
| undefined
|
||||
>;
|
||||
};
|
||||
subplebbitAddress?: string;
|
||||
};
|
||||
|
||||
export const getCommentCommunityAddress = (comment?: unknown) => {
|
||||
@@ -17,18 +16,15 @@ export const getCommentCommunityAddress = (comment?: unknown) => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const record = comment as { communityAddress?: unknown; subplebbitAddress?: unknown };
|
||||
const record = comment as { communityAddress?: unknown };
|
||||
if (typeof record.communityAddress === 'string' && record.communityAddress) {
|
||||
return record.communityAddress;
|
||||
}
|
||||
if (typeof record.subplebbitAddress === 'string' && record.subplebbitAddress) {
|
||||
return record.subplebbitAddress;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const withResolvedReplyPages = (replies?: CommentWithLegacyCommunityAddress['replies']) => {
|
||||
const withResolvedReplyPages = (replies?: CommentWithCommunityAddress['replies']) => {
|
||||
if (!replies?.pages) {
|
||||
return replies;
|
||||
}
|
||||
@@ -82,7 +78,7 @@ const withResolvedReplyPages = (replies?: CommentWithLegacyCommunityAddress['rep
|
||||
};
|
||||
};
|
||||
|
||||
export const withResolvedCommentCommunityAddress = <T extends CommentWithLegacyCommunityAddress | undefined | null>(comment: T): T => {
|
||||
export const withResolvedCommentCommunityAddress = <T extends CommentWithCommunityAddress | undefined | null>(comment: T): T => {
|
||||
if (!comment) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { DirectoryCommunity } from '../../hooks/use-directories';
|
||||
import usePostNumberStore from '../../stores/use-post-number-store';
|
||||
import type { ExternalQuoteReference, ExternalQuoteSearchStatus } from './external-quote-utils';
|
||||
import { getExternalQuoteBoardAddress, getExternalQuoteBoardLabel } from './external-quote-utils';
|
||||
import { getCommentCommunityAddress } from './comment-utils';
|
||||
import { getBoardPath } from './route-utils';
|
||||
|
||||
const BOARD_FEED_SORT_TYPE = 'new';
|
||||
@@ -63,7 +64,7 @@ const findLoadedCommentByNumber = ({ number, communityAddress }: { number: numbe
|
||||
const comments = Object.values(communitiesPagesStore.getState().comments) as Array<Comment | undefined>;
|
||||
|
||||
return comments.find((comment) => {
|
||||
const address = (comment as { communityAddress?: string }).communityAddress || comment?.subplebbitAddress;
|
||||
const address = getCommentCommunityAddress(comment);
|
||||
return address === communityAddress && comment?.number === number && comment?.cid;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -163,7 +163,7 @@ const fetchWebpageThumbnail = async (url: string): Promise<string | undefined> =
|
||||
});
|
||||
html = response.data.slice(0, MAX_HTML_SIZE);
|
||||
} else {
|
||||
// some sites have CORS access, from which the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
|
||||
// some sites have CORS access, so the thumbnail can be fetched client-side when community thumbnail fetching is disabled
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Comment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import communitiesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/communities';
|
||||
import { getCommentCommunityAddress } from './comment-utils';
|
||||
|
||||
type CommunityLike = {
|
||||
roles?: Record<string, { role?: string }>;
|
||||
@@ -142,20 +143,20 @@ export const displayNameMatchesPattern = (comment: Comment, pattern: string): bo
|
||||
* @returns True if the user has the specified role, false otherwise
|
||||
*/
|
||||
export const userHasRole = (comment: Comment, role: string): boolean => {
|
||||
const communityAddress = (comment as { communityAddress?: string }).communityAddress ?? comment?.subplebbitAddress;
|
||||
const communityAddress = getCommentCommunityAddress(comment);
|
||||
|
||||
if (!role || !comment?.author?.address || !communityAddress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const communities = communitiesStore.getState().communities;
|
||||
const subplebbit = communities[communityAddress] as CommunityLike | undefined;
|
||||
const community = communities[communityAddress] as CommunityLike | undefined;
|
||||
|
||||
if (!subplebbit?.roles) {
|
||||
if (!community?.roles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userRole = subplebbit.roles[comment.author.address]?.role;
|
||||
const userRole = community.roles[comment.author.address]?.role;
|
||||
|
||||
// Handle different role names (moderator/mod)
|
||||
if ((role.toLowerCase() === 'moderator' || role.toLowerCase() === 'mod') && userRole === 'moderator') {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const approvePendingCommentModeration = { approved: true } as const;
|
||||
|
||||
// plebbit-js clears pendingApproval only when rejection is published as approved:false.
|
||||
// pkc-js clears pendingApproval only when rejection is published as approved:false.
|
||||
// Sending removed:true marks the comment removed but can leave it in the mod queue.
|
||||
export const rejectPendingCommentModeration = { approved: false } as const;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ export type PostMenuProps = {
|
||||
postCid?: string;
|
||||
parentCid?: string;
|
||||
communityAddress?: string;
|
||||
subplebbitAddress?: string;
|
||||
authorAddress?: string;
|
||||
link?: string;
|
||||
linkWidth?: number;
|
||||
@@ -24,7 +23,6 @@ export const selectPostMenuProps = (post?: Comment): PostMenuProps => {
|
||||
postCid: post?.postCid,
|
||||
parentCid: post?.parentCid,
|
||||
communityAddress,
|
||||
subplebbitAddress: post?.subplebbitAddress,
|
||||
authorAddress: post?.author?.address,
|
||||
link: post?.link,
|
||||
linkWidth: post?.linkWidth,
|
||||
|
||||
@@ -16,7 +16,6 @@ type CommunityIdentifierLike = {
|
||||
};
|
||||
|
||||
type LegacyFeedOptionsLike = {
|
||||
subplebbitAddresses?: string[];
|
||||
sortType: string;
|
||||
postsPerPage?: number;
|
||||
filter?: unknown;
|
||||
@@ -72,9 +71,6 @@ const getCommunityIdentifiers = (opts: FeedOptionsLike | LegacyFeedOptionsLike):
|
||||
if ('communityAddresses' in opts && Array.isArray(opts.communityAddresses)) {
|
||||
return opts.communityAddresses.map((communityAddress) => ({ name: communityAddress }));
|
||||
}
|
||||
if ('subplebbitAddresses' in opts && Array.isArray(opts.subplebbitAddresses)) {
|
||||
return opts.subplebbitAddresses.map((communityAddress) => ({ name: communityAddress }));
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
|
||||
@@ -104,11 +104,6 @@ export const getCommunityAddress = (boardIdentifier: string, communities: Direct
|
||||
return boardIdentifier;
|
||||
};
|
||||
|
||||
/**
|
||||
* Back-compat alias kept for route params and comments.
|
||||
*/
|
||||
export const getSubplebbitAddress = getCommunityAddress;
|
||||
|
||||
/**
|
||||
* Compare two addresses; returns true if they refer to the same board (handles .bso/.eth aliases).
|
||||
*/
|
||||
|
||||
@@ -64,15 +64,15 @@ export const is5chanLink = (url: string): boolean => {
|
||||
|
||||
// For pleb.bz, only support the exact sharelink format (legacy /p/... format)
|
||||
if (hostname === 'pleb.bz') {
|
||||
// Must match exactly: /p/{subplebbitAddress}/c/{cid}
|
||||
// Must match exactly: /p/{communityAddress}/c/{cid}
|
||||
// Allow redirect parameter since these are still valid internal links
|
||||
return /^\/p\/[^/]+\/c\/[^/]+$/.test(routePath);
|
||||
}
|
||||
|
||||
// For other 5chan hostnames, support both old and new formats:
|
||||
// Old format (for backward compatibility):
|
||||
// - /p/{subplebbitAddress}
|
||||
// - /p/{subplebbitAddress}/c/{commentCid}
|
||||
// - /p/{communityAddress}
|
||||
// - /p/{communityAddress}/c/{commentCid}
|
||||
// New format:
|
||||
// - /{boardIdentifier} (directory code or address)
|
||||
// - /{boardIdentifier}/thread/{commentCid}
|
||||
|
||||
@@ -4,7 +4,6 @@ type ParamsType = {
|
||||
accountCommentIndex?: string;
|
||||
boardIdentifier?: string;
|
||||
commentCid?: string;
|
||||
subplebbitAddress?: string; // deprecated, kept for backward compatibility
|
||||
};
|
||||
|
||||
export const isAllView = (pathname: string): boolean => {
|
||||
@@ -28,13 +27,13 @@ export const isBoardView = (pathname: string, params: ParamsType): boolean => {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const identifier = params.boardIdentifier || params.subplebbitAddress;
|
||||
const identifier = params.boardIdentifier;
|
||||
return identifier ? decodedPathname.startsWith(`/${identifier}`) : false;
|
||||
};
|
||||
|
||||
export const isCatalogView = (pathname: string, params: ParamsType): boolean => {
|
||||
const { boardIdentifier, subplebbitAddress } = params;
|
||||
const identifier = boardIdentifier || subplebbitAddress;
|
||||
const { boardIdentifier } = params;
|
||||
const identifier = boardIdentifier;
|
||||
const decodedPathname = decodeURIComponent(pathname);
|
||||
|
||||
return (
|
||||
@@ -66,13 +65,13 @@ export const isPendingPostView = (pathname: string, params: ParamsType): boolean
|
||||
|
||||
export const isPostPageView = (pathname: string, params: ParamsType): boolean => {
|
||||
const decodedPathname = decodeURIComponent(pathname);
|
||||
const identifier = params.boardIdentifier || params.subplebbitAddress;
|
||||
const identifier = params.boardIdentifier;
|
||||
return identifier && params.commentCid ? decodedPathname.startsWith(`/${identifier}/thread/${params.commentCid}`) : false;
|
||||
};
|
||||
|
||||
export const isSettingsView = (pathname: string, params: ParamsType): boolean => {
|
||||
const { accountCommentIndex, boardIdentifier, commentCid, subplebbitAddress } = params;
|
||||
const identifier = boardIdentifier || subplebbitAddress;
|
||||
const { accountCommentIndex, boardIdentifier, commentCid } = params;
|
||||
const identifier = boardIdentifier;
|
||||
const decodedPathname = decodeURIComponent(pathname);
|
||||
return (
|
||||
(identifier && commentCid && decodedPathname === `/${identifier}/thread/${commentCid}/settings`) || decodedPathname === `/pending/${accountCommentIndex}/settings`
|
||||
@@ -84,8 +83,8 @@ export const isSubscriptionsView = (pathname: string, params: ParamsType): boole
|
||||
};
|
||||
|
||||
export const isArchiveView = (pathname: string, params: ParamsType): boolean => {
|
||||
const { boardIdentifier, subplebbitAddress } = params;
|
||||
const identifier = boardIdentifier || subplebbitAddress;
|
||||
const { boardIdentifier } = params;
|
||||
const identifier = boardIdentifier;
|
||||
const decodedPathname = decodeURIComponent(pathname);
|
||||
|
||||
return Boolean(identifier && isArchiveRoute(decodedPathname) && decodedPathname === `/${identifier}/archive`);
|
||||
|
||||
Reference in New Issue
Block a user