Skip to content

Commit 73371c0

Browse files
committed
fix: multi-challenge support and TypeScript build for iframe auto-answer
- Fix TypeScript implicit any error by adding type annotations - Only auto-submit when all challenges are url/iframe type - Collect answers positionally from each iframe via postMessage - Use 'challengeanswer' (lowercase) as message type
1 parent 7ea1233 commit 73371c0

2 files changed

Lines changed: 68 additions & 11 deletions

File tree

dist/hooks/actions/actions.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var __rest = (this && this.__rest) || function (s, e) {
1818
}
1919
return t;
2020
};
21-
import { useMemo, useState } from 'react';
21+
import { useEffect, useMemo, useState } from 'react';
2222
import useAccountsStore from '../../stores/accounts';
2323
import Logger from '@plebbit/plebbit-logger';
2424
const log = Logger('plebbit-react-hooks:actions:hooks');
@@ -27,6 +27,42 @@ import { useAccount, useAccountId } from '../accounts';
2727
const publishChallengeAnswersNotReady = (challengeAnswers) => __awaiter(void 0, void 0, void 0, function* () {
2828
throw Error(`can't call publishChallengeAnswers() before result.challenge is defined (before the challenge message is received)`);
2929
});
30+
function useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) {
31+
const challenges = challenge === null || challenge === void 0 ? void 0 : challenge.challenges;
32+
const allIframe = (challenges === null || challenges === void 0 ? void 0 : challenges.length) ? challenges.every((c) => c.type === 'url/iframe') : false;
33+
useEffect(() => {
34+
if (!allIframe || !challenges || !publishChallengeAnswers)
35+
return;
36+
const origins = challenges.map((c) => {
37+
try {
38+
return new URL(c.challenge).origin;
39+
}
40+
catch (e) {
41+
return undefined;
42+
}
43+
});
44+
const answers = new Array(challenges.length).fill(undefined);
45+
let submitted = false;
46+
const handleMessage = (event) => {
47+
var _a;
48+
if (submitted)
49+
return;
50+
if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) !== 'challengeanswer' || typeof event.data.challengeAnswer !== 'string')
51+
return;
52+
// find which iframe sent this by matching origin to first unfilled slot
53+
const index = origins.findIndex((origin, i) => answers[i] === undefined && (!origin || origin === event.origin));
54+
if (index === -1)
55+
return;
56+
answers[index] = event.data.challengeAnswer;
57+
if (answers.every((a) => a !== undefined)) {
58+
submitted = true;
59+
publishChallengeAnswers(answers);
60+
}
61+
};
62+
window.addEventListener('message', handleMessage);
63+
return () => window.removeEventListener('message', handleMessage);
64+
}, [allIframe, challenges, publishChallengeAnswers]);
65+
}
3066
export function useSubscribe(options) {
3167
var _a;
3268
assert(!options || typeof options === 'object', `useSubscribe options argument '${options}' not an object`);
@@ -137,6 +173,7 @@ export function usePublishComment(options) {
137173
const [challenge, setChallenge] = useState();
138174
const [challengeVerification, setChallengeVerification] = useState();
139175
const [publishChallengeAnswers, setPublishChallengeAnswers] = useState();
176+
useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers);
140177
let initialState = 'initializing';
141178
// before the accountId and options is defined, nothing can happen
142179
if (accountId && options) {
@@ -201,6 +238,7 @@ export function usePublishVote(options) {
201238
const [challenge, setChallenge] = useState();
202239
const [challengeVerification, setChallengeVerification] = useState();
203240
const [publishChallengeAnswers, setPublishChallengeAnswers] = useState();
241+
useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers);
204242
let initialState = 'initializing';
205243
// before the accountId and options is defined, nothing can happen
206244
if (accountId && options) {
@@ -263,6 +301,7 @@ export function usePublishCommentEdit(options) {
263301
const [challenge, setChallenge] = useState();
264302
const [challengeVerification, setChallengeVerification] = useState();
265303
const [publishChallengeAnswers, setPublishChallengeAnswers] = useState();
304+
useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers);
266305
let initialState = 'initializing';
267306
// before the accountId and options is defined, nothing can happen
268307
if (accountId && options) {
@@ -325,6 +364,7 @@ export function usePublishCommentModeration(options) {
325364
const [challenge, setChallenge] = useState();
326365
const [challengeVerification, setChallengeVerification] = useState();
327366
const [publishChallengeAnswers, setPublishChallengeAnswers] = useState();
367+
useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers);
328368
let initialState = 'initializing';
329369
// before the accountId and options is defined, nothing can happen
330370
if (accountId && options) {
@@ -387,6 +427,7 @@ export function usePublishSubplebbitEdit(options) {
387427
const [challenge, setChallenge] = useState();
388428
const [challengeVerification, setChallengeVerification] = useState();
389429
const [publishChallengeAnswers, setPublishChallengeAnswers] = useState();
430+
useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers);
390431
let initialState = 'initializing';
391432
// before the accountId and options is defined, nothing can happen
392433
if (accountId && subplebbitAddress) {

src/hooks/actions/actions.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,41 @@ const publishChallengeAnswersNotReady: PublishChallengeAnswers = async (challeng
3737
}
3838

3939
function useIframeChallengeAutoAnswer(challenge: Challenge | undefined, publishChallengeAnswers: PublishChallengeAnswers | undefined) {
40-
const iframeChallengeUrl = challenge?.challenges?.find((c) => c.type === 'url/iframe')?.challenge
40+
const challenges: {type: string; challenge: string}[] | undefined = challenge?.challenges
41+
const allIframe = challenges?.length ? challenges.every((c: {type: string; challenge: string}) => c.type === 'url/iframe') : false
4142

4243
useEffect(() => {
43-
if (!iframeChallengeUrl || !publishChallengeAnswers) return
44+
if (!allIframe || !challenges || !publishChallengeAnswers) return
4445

45-
let iframeOrigin: string | undefined
46-
try {
47-
iframeOrigin = new URL(iframeChallengeUrl).origin
48-
} catch (e) {}
46+
const origins = challenges.map((c: {type: string; challenge: string}) => {
47+
try {
48+
return new URL(c.challenge).origin
49+
} catch (e) {
50+
return undefined
51+
}
52+
})
53+
54+
const answers: (string | undefined)[] = new Array(challenges.length).fill(undefined)
55+
let submitted = false
4956

5057
const handleMessage = (event: MessageEvent) => {
51-
if (iframeOrigin && event.origin !== iframeOrigin) return
52-
if (event.data?.type === 'challengeAnswer' && Array.isArray(event.data.challengeAnswers)) {
53-
publishChallengeAnswers(event.data.challengeAnswers)
58+
if (submitted) return
59+
if (event.data?.type !== 'challengeanswer' || typeof event.data.challengeAnswer !== 'string') return
60+
61+
// find which iframe sent this by matching origin to first unfilled slot
62+
const index = origins.findIndex((origin, i) => answers[i] === undefined && (!origin || origin === event.origin))
63+
if (index === -1) return
64+
65+
answers[index] = event.data.challengeAnswer
66+
67+
if (answers.every((a) => a !== undefined)) {
68+
submitted = true
69+
publishChallengeAnswers(answers as string[])
5470
}
5571
}
5672
window.addEventListener('message', handleMessage)
5773
return () => window.removeEventListener('message', handleMessage)
58-
}, [iframeChallengeUrl, publishChallengeAnswers])
74+
}, [allIframe, challenges, publishChallengeAnswers])
5975
}
6076

6177
export function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult {

0 commit comments

Comments
 (0)