mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(challenge-modal): publish challenge answers with pkc object schema
pkc-js's publishChallengeAnswers destructures { challengeAnswers } from its
argument, so passing a bare string[] left challengeAnswers undefined and
silently broke challenge submission (i.e. posting). Wrap answers in the object
form via a publishPublicationChallengeAnswers helper with error logging, and
align the ChallengePublication type with the real runtime signature.
This commit is contained in:
@@ -49,11 +49,15 @@ vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
|||||||
useComment: ({ commentCid }: { commentCid?: string }) => (commentCid ? testState.commentsByCid[commentCid] : undefined),
|
useComment: ({ commentCid }: { commentCid?: string }) => (commentCid ? testState.commentsByCid[commentCid] : undefined),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../../lib/utils/challenge-utils', () => ({
|
vi.mock('../../../lib/utils/challenge-utils', async (importOriginal) => {
|
||||||
getPublicationPreview: () => testState.publicationPreview,
|
const actual = await importOriginal<typeof import('../../../lib/utils/challenge-utils')>();
|
||||||
getPublicationType: () => testState.publicationType,
|
return {
|
||||||
getVotePreview: () => testState.votePreview,
|
...actual,
|
||||||
}));
|
getPublicationPreview: () => testState.publicationPreview,
|
||||||
|
getPublicationType: () => testState.publicationType,
|
||||||
|
getVotePreview: () => testState.votePreview,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
vi.mock('../../../hooks/use-is-mobile', () => ({
|
vi.mock('../../../hooks/use-is-mobile', () => ({
|
||||||
default: () => testState.isMobile,
|
default: () => testState.isMobile,
|
||||||
@@ -224,7 +228,7 @@ describe('ChallengeModal', () => {
|
|||||||
input?.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' }));
|
input?.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' }));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith(['4']);
|
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith({ challengeAnswers: ['4'] });
|
||||||
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -310,7 +314,7 @@ describe('ChallengeModal', () => {
|
|||||||
await dispatchInput(container.querySelector<HTMLInputElement>('input[placeholder*="TYPE THE ANSWER HERE"]') as HTMLInputElement, 'step two');
|
await dispatchInput(container.querySelector<HTMLInputElement>('input[placeholder*="TYPE THE ANSWER HERE"]') as HTMLInputElement, 'step two');
|
||||||
await clickButton('submit');
|
await clickButton('submit');
|
||||||
|
|
||||||
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith(['step one', 'step two']);
|
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith({ challengeAnswers: ['step one', 'step two'] });
|
||||||
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -369,7 +373,7 @@ describe('ChallengeModal', () => {
|
|||||||
expect(doneButton?.getAttribute('aria-label')).toBe('Finish challenge');
|
expect(doneButton?.getAttribute('aria-label')).toBe('Finish challenge');
|
||||||
|
|
||||||
await clickButton('Finish Challenge');
|
await clickButton('Finish Challenge');
|
||||||
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith(['']);
|
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith({ challengeAnswers: [''] });
|
||||||
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -481,7 +485,7 @@ describe('ChallengeModal', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith(['']);
|
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith({ challengeAnswers: [''] });
|
||||||
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -538,7 +542,7 @@ describe('ChallengeModal', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith(['', '']);
|
expect(publication.publishChallengeAnswers).toHaveBeenCalledWith({ challengeAnswers: ['', ''] });
|
||||||
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
expect(testState.removeChallengeMock).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useRef, useState, useEffect, useCallback } from 'react';
|
import { useRef, useState, useEffect, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Challenge as ChallengeType, useAccount, useComment } from '@bitsocial/bitsocial-react-hooks';
|
import { Challenge as ChallengeType, useAccount, useComment } from '@bitsocial/bitsocial-react-hooks';
|
||||||
import { getPublicationPreview, getPublicationType, getVotePreview } from '../../lib/utils/challenge-utils';
|
import { getPublicationPreview, getPublicationType, getVotePreview, publishPublicationChallengeAnswers } from '../../lib/utils/challenge-utils';
|
||||||
import { stripGeneratedFortuneMarkup } from '../../lib/utils/post-options-utils';
|
import { stripGeneratedFortuneMarkup } from '../../lib/utils/post-options-utils';
|
||||||
import useIsMobile from '../../hooks/use-is-mobile';
|
import useIsMobile from '../../hooks/use-is-mobile';
|
||||||
import useChallengesStore from '../../stores/use-challenges-store';
|
import useChallengesStore from '../../stores/use-challenges-store';
|
||||||
@@ -40,6 +40,10 @@ const ImageChallenge = ({ challenge }: { challenge: string }) =>
|
|||||||
<div className={styles.challengeMedia}>Invalid image challenge</div>
|
<div className={styles.challengeMedia}>Invalid image challenge</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const logPublishChallengeAnswersError = (error: unknown) => {
|
||||||
|
console.error('Failed to publish challenge answers:', error);
|
||||||
|
};
|
||||||
|
|
||||||
const isLocalIframeHostname = (hostname: string) => hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]' || hostname.endsWith('.localhost');
|
const isLocalIframeHostname = (hostname: string) => hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]' || hostname.endsWith('.localhost');
|
||||||
|
|
||||||
type ValidatedIframeUrl = { status: 'valid'; finalUrl: string; origin: string } | { status: 'invalid'; error: unknown } | { status: 'unsupported' };
|
type ValidatedIframeUrl = { status: 'valid'; finalUrl: string; origin: string } | { status: 'invalid'; error: unknown } | { status: 'unsupported' };
|
||||||
@@ -391,7 +395,7 @@ const Challenge = ({ challenge, closeModal, abandonModal }: ChallengeProps) => {
|
|||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
if (!publication) return;
|
if (!publication) return;
|
||||||
publication.publishChallengeAnswers(answers);
|
void publishPublicationChallengeAnswers(publication, answers).catch(logPublishChallengeAnswersError);
|
||||||
setAnswers([]);
|
setAnswers([]);
|
||||||
closeModal();
|
closeModal();
|
||||||
};
|
};
|
||||||
@@ -413,7 +417,7 @@ const Challenge = ({ challenge, closeModal, abandonModal }: ChallengeProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
publication.publishChallengeAnswers(updatedAnswers);
|
void publishPublicationChallengeAnswers(publication, updatedAnswers).catch(logPublishChallengeAnswersError);
|
||||||
setAnswers([]);
|
setAnswers([]);
|
||||||
closeModal();
|
closeModal();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,19 +13,28 @@ const resolveBoardIdentifier = (communityAddress: unknown): string => {
|
|||||||
return boardPath === communityAddress ? communityAddress : `/${boardPath}/`;
|
return boardPath === communityAddress ? communityAddress : `/${boardPath}/`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChallengePublication = Partial<Comment> & {
|
type ChallengeAnswersInput = string[] | { challengeAnswers?: string[] };
|
||||||
|
|
||||||
|
export type ChallengePublication = Partial<Omit<Comment, 'publishChallengeAnswers'>> & {
|
||||||
author?: unknown;
|
author?: unknown;
|
||||||
commentCid?: string;
|
commentCid?: string;
|
||||||
communityAddress?: string;
|
communityAddress?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
link?: string;
|
link?: string;
|
||||||
parentCid?: string;
|
parentCid?: string;
|
||||||
|
publishChallengeAnswers?: (challengeAnswers?: ChallengeAnswersInput) => Promise<void> | void;
|
||||||
shortCommunityAddress?: string;
|
shortCommunityAddress?: string;
|
||||||
subplebbitAddress?: string;
|
subplebbitAddress?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
vote?: number;
|
vote?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const publishPublicationChallengeAnswers = async (publication: ChallengePublication | undefined, challengeAnswers: string[]) => {
|
||||||
|
const publishChallengeAnswers = publication?.publishChallengeAnswers;
|
||||||
|
if (typeof publishChallengeAnswers !== 'function') return;
|
||||||
|
await publishChallengeAnswers.call(publication, { challengeAnswers });
|
||||||
|
};
|
||||||
|
|
||||||
export const redactGeneratedFortuneFromPublication = <T>(publication: T): T => {
|
export const redactGeneratedFortuneFromPublication = <T>(publication: T): T => {
|
||||||
if (!publication || typeof publication !== 'object') {
|
if (!publication || typeof publication !== 'object') {
|
||||||
return publication;
|
return publication;
|
||||||
|
|||||||
Reference in New Issue
Block a user