diff --git a/dist/hooks/actions/actions.js b/dist/hooks/actions/actions.js index 95705160..8797a23a 100644 --- a/dist/hooks/actions/actions.js +++ b/dist/hooks/actions/actions.js @@ -18,7 +18,7 @@ var __rest = (this && this.__rest) || function (s, e) { } return t; }; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import useAccountsStore from '../../stores/accounts'; import Logger from '@plebbit/plebbit-logger'; const log = Logger('plebbit-react-hooks:actions:hooks'); @@ -27,6 +27,42 @@ import { useAccount, useAccountId } from '../accounts'; const publishChallengeAnswersNotReady = (challengeAnswers) => __awaiter(void 0, void 0, void 0, function* () { throw Error(`can't call publishChallengeAnswers() before result.challenge is defined (before the challenge message is received)`); }); +function useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) { + const challenges = challenge === null || challenge === void 0 ? void 0 : challenge.challenges; + const allIframe = (challenges === null || challenges === void 0 ? void 0 : challenges.length) ? challenges.every((c) => c.type === 'url/iframe') : false; + useEffect(() => { + if (!allIframe || !challenges || !publishChallengeAnswers) + return; + const origins = challenges.map((c) => { + try { + return new URL(c.challenge).origin; + } + catch (e) { + return undefined; + } + }); + const answers = new Array(challenges.length).fill(undefined); + let submitted = false; + const handleMessage = (event) => { + var _a; + if (submitted) + return; + if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) !== 'challengeanswer' || typeof event.data.challengeAnswer !== 'string') + return; + // find which iframe sent this by matching origin to first unfilled slot + const index = origins.findIndex((origin, i) => answers[i] === undefined && (!origin || origin === event.origin)); + if (index === -1) + return; + answers[index] = event.data.challengeAnswer; + if (answers.every((a) => a !== undefined)) { + submitted = true; + publishChallengeAnswers(answers); + } + }; + window.addEventListener('message', handleMessage); + return () => window.removeEventListener('message', handleMessage); + }, [allIframe, challenges, publishChallengeAnswers]); +} export function useSubscribe(options) { var _a; assert(!options || typeof options === 'object', `useSubscribe options argument '${options}' not an object`); @@ -137,6 +173,7 @@ export function usePublishComment(options) { const [challenge, setChallenge] = useState(); const [challengeVerification, setChallengeVerification] = useState(); const [publishChallengeAnswers, setPublishChallengeAnswers] = useState(); + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers); let initialState = 'initializing'; // before the accountId and options is defined, nothing can happen if (accountId && options) { @@ -201,6 +238,7 @@ export function usePublishVote(options) { const [challenge, setChallenge] = useState(); const [challengeVerification, setChallengeVerification] = useState(); const [publishChallengeAnswers, setPublishChallengeAnswers] = useState(); + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers); let initialState = 'initializing'; // before the accountId and options is defined, nothing can happen if (accountId && options) { @@ -263,6 +301,7 @@ export function usePublishCommentEdit(options) { const [challenge, setChallenge] = useState(); const [challengeVerification, setChallengeVerification] = useState(); const [publishChallengeAnswers, setPublishChallengeAnswers] = useState(); + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers); let initialState = 'initializing'; // before the accountId and options is defined, nothing can happen if (accountId && options) { @@ -325,6 +364,7 @@ export function usePublishCommentModeration(options) { const [challenge, setChallenge] = useState(); const [challengeVerification, setChallengeVerification] = useState(); const [publishChallengeAnswers, setPublishChallengeAnswers] = useState(); + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers); let initialState = 'initializing'; // before the accountId and options is defined, nothing can happen if (accountId && options) { @@ -387,6 +427,7 @@ export function usePublishSubplebbitEdit(options) { const [challenge, setChallenge] = useState(); const [challengeVerification, setChallengeVerification] = useState(); const [publishChallengeAnswers, setPublishChallengeAnswers] = useState(); + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers); let initialState = 'initializing'; // before the accountId and options is defined, nothing can happen if (accountId && subplebbitAddress) { diff --git a/src/hooks/actions/actions.ts b/src/hooks/actions/actions.ts index 23ffad50..8f47fa73 100644 --- a/src/hooks/actions/actions.ts +++ b/src/hooks/actions/actions.ts @@ -1,4 +1,4 @@ -import {useMemo, useState} from 'react' +import {useEffect, useMemo, useState} from 'react' import useAccountsStore from '../../stores/accounts' import Logger from '@plebbit/plebbit-logger' const log = Logger('plebbit-react-hooks:actions:hooks') @@ -36,6 +36,44 @@ const publishChallengeAnswersNotReady: PublishChallengeAnswers = async (challeng throw Error(`can't call publishChallengeAnswers() before result.challenge is defined (before the challenge message is received)`) } +function useIframeChallengeAutoAnswer(challenge: Challenge | undefined, publishChallengeAnswers: PublishChallengeAnswers | undefined) { + const challenges: {type: string; challenge: string}[] | undefined = challenge?.challenges + const allIframe = challenges?.length ? challenges.every((c: {type: string; challenge: string}) => c.type === 'url/iframe') : false + + useEffect(() => { + if (!allIframe || !challenges || !publishChallengeAnswers) return + + const origins = challenges.map((c: {type: string; challenge: string}) => { + try { + return new URL(c.challenge).origin + } catch (e) { + return undefined + } + }) + + const answers: (string | undefined)[] = new Array(challenges.length).fill(undefined) + let submitted = false + + const handleMessage = (event: MessageEvent) => { + if (submitted) return + if (event.data?.type !== 'challengeanswer' || typeof event.data.challengeAnswer !== 'string') return + + // find which iframe sent this by matching origin to first unfilled slot + const index = origins.findIndex((origin, i) => answers[i] === undefined && (!origin || origin === event.origin)) + if (index === -1) return + + answers[index] = event.data.challengeAnswer + + if (answers.every((a) => a !== undefined)) { + submitted = true + publishChallengeAnswers(answers as string[]) + } + } + window.addEventListener('message', handleMessage) + return () => window.removeEventListener('message', handleMessage) + }, [allIframe, challenges, publishChallengeAnswers]) +} + export function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult { assert(!options || typeof options === 'object', `useSubscribe options argument '${options}' not an object`) const {subplebbitAddress, accountName, onError} = options || {} @@ -155,6 +193,7 @@ export function usePublishComment(options?: UsePublishCommentOptions): UsePublis const [challenge, setChallenge] = useState() const [challengeVerification, setChallengeVerification] = useState() const [publishChallengeAnswers, setPublishChallengeAnswers] = useState() + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) let initialState = 'initializing' // before the accountId and options is defined, nothing can happen @@ -228,6 +267,7 @@ export function usePublishVote(options?: UsePublishVoteOptions): UsePublishVoteR const [challenge, setChallenge] = useState() const [challengeVerification, setChallengeVerification] = useState() const [publishChallengeAnswers, setPublishChallengeAnswers] = useState() + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) let initialState = 'initializing' // before the accountId and options is defined, nothing can happen @@ -299,6 +339,7 @@ export function usePublishCommentEdit(options?: UsePublishCommentEditOptions): U const [challenge, setChallenge] = useState() const [challengeVerification, setChallengeVerification] = useState() const [publishChallengeAnswers, setPublishChallengeAnswers] = useState() + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) let initialState = 'initializing' // before the accountId and options is defined, nothing can happen @@ -370,6 +411,7 @@ export function usePublishCommentModeration(options?: UsePublishCommentModeratio const [challenge, setChallenge] = useState() const [challengeVerification, setChallengeVerification] = useState() const [publishChallengeAnswers, setPublishChallengeAnswers] = useState() + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) let initialState = 'initializing' // before the accountId and options is defined, nothing can happen @@ -441,6 +483,7 @@ export function usePublishSubplebbitEdit(options?: UsePublishSubplebbitEditOptio const [challenge, setChallenge] = useState() const [challengeVerification, setChallengeVerification] = useState() const [publishChallengeAnswers, setPublishChallengeAnswers] = useState() + useIframeChallengeAutoAnswer(challenge, publishChallengeAnswers) let initialState = 'initializing' // before the accountId and options is defined, nothing can happen