Merge branch 'codex/fix/trash-transfer-publication-identity'

This commit is contained in:
Tommaso Casaburi
2026-07-04 23:48:31 +07:00
5 changed files with 105 additions and 8 deletions
@@ -165,6 +165,31 @@ describe('PostTransferModal', () => {
expect(options).toContainEqual({ text: TRASH_BOARD_TITLE, value: TRASH_BOARD_ADDRESS });
});
it('publishes hidden trash board transfers with the board public key identity', async () => {
await renderTransferModal();
const select = document.body.querySelector<HTMLSelectElement>('select');
expect(select).not.toBeNull();
await act(async () => {
select!.value = TRASH_BOARD_ADDRESS;
select!.dispatchEvent(new Event('change', { bubbles: true }));
});
const form = document.body.querySelector<HTMLFormElement>('form');
expect(form).not.toBeNull();
await act(async () => {
form!.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
await Promise.resolve();
});
const [publishOptions] = testState.publishCommentMock.mock.calls[0] as [Record<string, unknown>, string];
expect(publishOptions).toMatchObject({
communityAddress: TRASH_BOARD_PUBLIC_KEY,
communityPublicKey: TRASH_BOARD_PUBLIC_KEY,
});
expect(publishOptions).not.toHaveProperty('communityName');
});
it('resolves the hidden trash board when it is the transfer source', async () => {
await renderTransferModal({ ...baseComment, communityAddress: TRASH_BOARD_ADDRESS });
@@ -192,6 +217,11 @@ describe('PostTransferModal', () => {
});
const [sourceModerationOptions] = testState.publishCommentModerationMock.mock.calls[1] as [{ commentModeration: { reason: string } }, string];
expect(sourceModerationOptions).toMatchObject({
communityAddress: TRASH_BOARD_PUBLIC_KEY,
communityPublicKey: TRASH_BOARD_PUBLIC_KEY,
});
expect(sourceModerationOptions).not.toHaveProperty('communityName');
expect(sourceModerationOptions.commentModeration.reason).toContain('/trash/');
expect(sourceModerationOptions.commentModeration.reason).toContain('(the rules)');
});
@@ -19,6 +19,7 @@ import {
getInitialTransferFields,
getTargetTransferModerationFlairs,
getTransferBoardReference,
getTransferPublishIdentity,
getTransferPublishPayload,
getTransferSourceBoardReference,
getTransferSourceBoardRulesLink,
@@ -344,7 +345,9 @@ const PostTransferModal = ({ comment, onClose, onTransferStateChange, onTransfer
try {
await createAccount(temporaryAccountName);
temporaryAccountCreated = true;
const payload = getTransferPublishPayload(comment, selectedFields, resolvedTargetBoardAddress);
const targetPublishIdentity = getTransferPublishIdentity(resolvedTargetBoard, resolvedTargetBoardAddress);
const sourcePublishIdentity = sourceCommunityAddress ? getTransferPublishIdentity(sourceBoard, sourceCommunityAddress) : undefined;
const payload = getTransferPublishPayload(comment, selectedFields, resolvedTargetBoardAddress, resolvedTargetBoard);
await publishComment(
{
...payload,
@@ -371,11 +374,14 @@ const PostTransferModal = ({ comment, onClose, onTransferStateChange, onTransfer
if (!targetCommentCid) {
throw new Error('Transferred post was accepted, but no target CID was returned.');
}
if (!sourcePublishIdentity) {
throw new Error('Transferred post was accepted, but no source board was resolved.');
}
// Queue the target marker first so a target moderation failure does not remove the original post.
await publishCommentModeration({
commentCid: targetCommentCid,
communityAddress: resolvedTargetBoardAddress,
...targetPublishIdentity,
commentModeration: {
flairs: getTargetTransferModerationFlairs(comment, selectedFields),
},
@@ -383,7 +389,7 @@ const PostTransferModal = ({ comment, onClose, onTransferStateChange, onTransfer
});
await publishCommentModeration({
commentCid: sourceCommentCid,
communityAddress: sourceCommunityAddress,
...sourcePublishIdentity,
commentModeration: getTransferSourceModeration(
comment,
getTransferBoardReference(resolvedTargetBoard, resolvedTargetBoardAddress),
+32 -1
View File
@@ -3,13 +3,14 @@ import {
canTransferComment,
getTargetTransferModerationFlairs,
getTransferPublishPayload,
getTransferPublishIdentity,
getTransferSourceBoardReference,
getTransferSourceBoardRulesLink,
getTransferSourceModeration,
hasTransferredCommentMarker,
TRANSFERRED_COMMENT_FLAIR,
} from '../comment-transfer';
import { TRASH_BOARD_ADDRESS, TRASH_BOARD_CODE, TRASH_BOARD_TITLE } from '../special-boards';
import { TRASH_BOARD_ADDRESS, TRASH_BOARD_CODE, TRASH_BOARD_PUBLIC_KEY, TRASH_BOARD_TITLE } from '../special-boards';
describe('comment-transfer', () => {
const selectedFields = {
@@ -58,6 +59,36 @@ describe('comment-transfer', () => {
});
});
it('uses the public key identity when publishing transfers to a special board', () => {
expect(getTransferPublishIdentity({ address: 'tech-posting.eth', directoryCode: 'g' }, 'tech-posting.eth')).toEqual({
communityAddress: 'tech-posting.eth',
});
expect(getTransferPublishIdentity({ address: TRASH_BOARD_ADDRESS, directoryCode: TRASH_BOARD_CODE, publicKey: TRASH_BOARD_PUBLIC_KEY }, TRASH_BOARD_ADDRESS)).toEqual(
{
communityAddress: TRASH_BOARD_PUBLIC_KEY,
communityPublicKey: TRASH_BOARD_PUBLIC_KEY,
},
);
const payload = getTransferPublishPayload(
{
cid: 'source',
communityAddress: 'music-posting.eth',
content: 'wrong board',
} as never,
selectedFields,
TRASH_BOARD_ADDRESS,
{ address: TRASH_BOARD_ADDRESS, directoryCode: TRASH_BOARD_CODE, publicKey: TRASH_BOARD_PUBLIC_KEY },
);
expect(payload).toMatchObject({
communityAddress: TRASH_BOARD_PUBLIC_KEY,
communityPublicKey: TRASH_BOARD_PUBLIC_KEY,
content: 'wrong board',
});
expect(payload).not.toHaveProperty('communityName');
});
it('copies selected comment body text verbatim while still ignoring blank bodies', () => {
expect(
getTransferPublishPayload(
+30 -2
View File
@@ -10,9 +10,16 @@ export type PostTransferFields = Record<PostTransferField, boolean>;
export type TransferBoardLike = {
address?: string;
directoryCode?: string;
publicKey?: string;
title?: string;
};
export type TransferPublishIdentity = {
communityAddress: string;
communityPublicKey?: string;
communityName?: string;
};
export const TRANSFERRED_COMMENT_FLAIR_TEXT = '5chan:transferred';
export const TRANSFERRED_COMMENT_FLAIR = { text: TRANSFERRED_COMMENT_FLAIR_TEXT } as const;
export const TRANSFER_FIELD_KEYS: PostTransferField[] = ['displayName', 'title', 'content', 'link', 'spoiler', 'flairs'];
@@ -63,9 +70,30 @@ export const getAvailableTransferFields = (comment: Comment): PostTransferField[
export const hasSelectedTransferFields = (fields: PostTransferFields, availableFields: PostTransferField[]) => availableFields.some((field) => fields[field]);
export const getTransferPublishPayload = (comment: Comment, fields: PostTransferFields, targetBoardAddress: string): Record<string, unknown> => {
export const getTransferPublishIdentity = (targetBoard: TransferBoardLike | undefined, targetBoardAddress: string): TransferPublishIdentity => {
const boardAddress = getTextField(targetBoard?.address) ?? targetBoardAddress;
const publicKey = getTextField(targetBoard?.publicKey);
if (publicKey && isSpecialBoardCode(targetBoard?.directoryCode)) {
return {
communityAddress: publicKey,
communityPublicKey: publicKey,
};
}
return {
communityAddress: boardAddress,
};
};
export const getTransferPublishPayload = (
comment: Comment,
fields: PostTransferFields,
targetBoardAddress: string,
targetBoard?: TransferBoardLike,
): Record<string, unknown> => {
const payload: Record<string, unknown> = {
communityAddress: targetBoardAddress,
...getTransferPublishIdentity(targetBoard, targetBoardAddress),
};
const displayName = getCommentDisplayName(comment);
const title = getTextField(comment.title);
+4 -2
View File
@@ -3,6 +3,7 @@ export interface SpecialBoard {
aliases?: string[];
directoryCode: string;
nsfw?: boolean;
publicKey?: string;
title: string;
}
@@ -14,14 +15,15 @@ export const TRASH_BOARD_TITLE = '/trash/ - Off-topic';
export const SPECIAL_BOARDS: SpecialBoard[] = [
{
address: TRASH_BOARD_ADDRESS,
aliases: [TRASH_BOARD_PUBLIC_KEY, 'off-topic.eth'],
aliases: ['off-topic.eth'],
directoryCode: TRASH_BOARD_CODE,
nsfw: true,
publicKey: TRASH_BOARD_PUBLIC_KEY,
title: TRASH_BOARD_TITLE,
},
];
const getSpecialBoardLookupAddresses = (board: SpecialBoard): string[] => [board.address, ...(board.aliases ?? [])];
const getSpecialBoardLookupAddresses = (board: SpecialBoard): string[] => [board.address, ...(board.publicKey ? [board.publicKey] : []), ...(board.aliases ?? [])];
export const getSpecialBoardByCode = (code: string | undefined): SpecialBoard | undefined =>
code ? SPECIAL_BOARDS.find((board) => board.directoryCode === code) : undefined;