Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions frontends/web/src/hooks/vendor-iframe-resize-height.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0

import { RefObject, useCallback, useEffect, useRef, useState } from 'react';

export type TVendorIframeResizeHeight = {
containerRef: RefObject<HTMLDivElement>;
height: number;
iframeLoaded: boolean;
iframeRef: RefObject<HTMLIFrameElement>;
onIframeLoad: () => void;
};

export const useVendorIframeResizeHeight = (): TVendorIframeResizeHeight => {
const containerRef = useRef<HTMLDivElement>(null);
const iframeRef = useRef<HTMLIFrameElement>(null);
const resizeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

const [height, setHeight] = useState(0);
const [iframeLoaded, setIframeLoaded] = useState(false);

const onResize = useCallback(() => {
if (resizeTimerRef.current) {
clearTimeout(resizeTimerRef.current);
}
resizeTimerRef.current = setTimeout(() => {
if (!containerRef.current) {
return;
}
setHeight(containerRef.current.offsetHeight);
}, 200);
}, []);

useEffect(() => {
onResize();
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
if (resizeTimerRef.current) {
clearTimeout(resizeTimerRef.current);
}
};
}, [onResize]);

const onIframeLoad = useCallback(() => {
setIframeLoaded(true);
onResize();
}, [onResize]);

return {
containerRef,
height,
iframeLoaded,
iframeRef,
onIframeLoad,
};
};
18 changes: 18 additions & 0 deletions frontends/web/src/hooks/vendor-iframe-terms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0

import { Dispatch, SetStateAction, useEffect, useState } from 'react';

type TVendorIframeTerms = {
agreedTerms: boolean;
setAgreedTerms: Dispatch<SetStateAction<boolean>>;
};

export const useVendorTerms = (agreedByConfig = false): TVendorIframeTerms => {
const [agreedTerms, setAgreedTerms] = useState(agreedByConfig);

useEffect(() => {
setAgreedTerms(agreedByConfig);
}, [agreedByConfig]);

return { agreedTerms, setAgreedTerms };
};
4 changes: 4 additions & 0 deletions frontends/web/src/hooks/vendor-iframe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-License-Identifier: Apache-2.0

export { useVendorIframeResizeHeight } from './vendor-iframe-resize-height';
export { useVendorTerms } from './vendor-iframe-terms';
46 changes: 10 additions & 36 deletions frontends/web/src/routes/bitsurance/widget.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

import { useState, useEffect, createRef, useRef } from 'react';
import { useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { RequestAddressV0Message, MessageVersion, parseMessage, serializeMessage, V0MessageType } from 'request-address';
Expand All @@ -16,6 +16,7 @@ import { alertUser } from '@/components/alert/Alert';
import { BitsuranceGuide } from './guide';
import { getBitsuranceURL } from '@/api/bitsurance';
import { convertScriptType } from '@/utils/request-addess';
import { useVendorIframeResizeHeight, useVendorTerms } from '@/hooks/vendor-iframe';
import style from './widget.module.css';

type TProps = {
Expand All @@ -26,48 +27,21 @@ export const BitsuranceWidget = ({ code }: TProps) => {
const navigate = useNavigate();
const { t } = useTranslation();

const [height, setHeight] = useState(0);
const [iframeLoaded, setIframeLoaded] = useState(false);
const [agreedTerms, setAgreedTerms] = useState(false);

const iframeURL = useLoad(getBitsuranceURL);
const config = useLoad(getConfig);
const accountInfo = useLoad(getInfo(code));

const ref = createRef<HTMLDivElement>();
const iframeRef = useRef<HTMLIFrameElement | null>(null);
let signing = false;
let resizeTimerID: any = undefined;
const { containerRef, height, iframeLoaded, iframeRef, onIframeLoad } = useVendorIframeResizeHeight();
const { agreedTerms, setAgreedTerms } = useVendorTerms(!!config?.frontend?.skipBitsuranceDisclaimer);
const signingRef = useRef(false);

useEffect(() => {
if (config) {
setAgreedTerms(config.frontend.skipBitsuranceDisclaimer);
}
}, [config]);

useEffect(() => {
onResize();
window.addEventListener('resize', onResize);
window.addEventListener('message', onMessage);

return () => {
window.removeEventListener('resize', onResize);
window.removeEventListener('message', onMessage);
};
});

const onResize = () => {
if (resizeTimerID) {
clearTimeout(resizeTimerID);
}
resizeTimerID = setTimeout(() => {
if (!ref.current) {
return;
}
setHeight(ref.current.offsetHeight);
}, 200);
};

const sendAddressWithXPub = (address: string, sig: string, xpub: string) => {
const { current } = iframeRef;

Expand All @@ -94,7 +68,7 @@ export const BitsuranceWidget = ({ code }: TProps) => {
};

const handleRequestAddress = (message: RequestAddressV0Message) => {
signing = true;
signingRef.current = true;
const addressType = message.withScriptType ? convertScriptType(message.withScriptType) : '';
const withMessageSignature = message.withMessageSignature ? message.withMessageSignature : '';
const withExtendedPublicKey = !!message.withExtendedPublicKey;
Expand All @@ -103,7 +77,7 @@ export const BitsuranceWidget = ({ code }: TProps) => {
withMessageSignature,
code)
.then(response => {
signing = false;
signingRef.current = false;
if (response.success) {
if (withExtendedPublicKey) {
const xpub = getXPub(addressType as ScriptType);
Expand Down Expand Up @@ -148,7 +122,7 @@ export const BitsuranceWidget = ({ code }: TProps) => {
case V0MessageType.RequestAddress:
// we ignore further signing requests
// while there is an ongoing one
if (!signing) {
if (!signingRef.current) {
handleRequestAddress(message);
}
break;
Expand All @@ -166,7 +140,7 @@ export const BitsuranceWidget = ({ code }: TProps) => {
<div className={style.header}>
<Header title={<h2>{t('bitsuranceAccount.title')}</h2>} />
</div>
<div ref={ref} className={style.container}>
<div ref={containerRef} className={style.container}>
{ !agreedTerms ? (
<BitsuranceTerms
onAgreedTerms={() => setAgreedTerms(true)}
Expand All @@ -177,7 +151,7 @@ export const BitsuranceWidget = ({ code }: TProps) => {
{!iframeLoaded && <Spinner text={t('loading')} /> }
<iframe
onLoad={() => {
setIframeLoaded(true);
onIframeLoad();
}}
ref={iframeRef}
title="Bitsurance"
Expand Down
42 changes: 5 additions & 37 deletions frontends/web/src/routes/market/bitrefill.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

import { useState, useEffect, useRef, useCallback, useContext } from 'react';
import { useState, useEffect, useCallback, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { Header, GuideWrapper, GuidedContent } from '@/components/layout';
import { Spinner } from '@/components/spinner/Spinner';
Expand All @@ -18,6 +18,7 @@ import { getBitrefillInfo } from '@/api/market';
import { getURLOrigin } from '@/utils/url';
import { ConfirmBitrefill } from './bitrefill-confirm';
import { AppContext } from '@/contexts/AppContext';
import { useVendorIframeResizeHeight, useVendorTerms } from '@/hooks/vendor-iframe';
import style from './iframe.module.css';

// Map coins supported by Bitrefill
Expand Down Expand Up @@ -46,49 +47,17 @@ export const Bitrefill = ({
const { isDevServers } = useContext(AppContext);
const account = findAccount(accounts, code);

const containerRef = useRef<HTMLDivElement>(null);
const iframeRef = useRef<HTMLIFrameElement | null>(null);
const [iframeLoaded, setIframeLoaded] = useState(false);
const [height, setHeight] = useState(0);
const resizeTimerID = useRef<ReturnType<typeof setTimeout> | null>(null);
const bitrefillInfo = useLoad(() => getBitrefillInfo('spend', code));

const config = useLoad(getConfig);
const [agreedTerms, setAgreedTerms] = useState(false);
const { containerRef, height, iframeLoaded, iframeRef, onIframeLoad } = useVendorIframeResizeHeight();
const { agreedTerms, setAgreedTerms } = useVendorTerms(!!config?.frontend?.skipBitrefillWidgetDisclaimer);

const [pendingPayment, setPendingPayment] = useState<boolean>(false);
const [verifyPaymentRequest, setVerifyPaymentRequest] = useState<TTxProposalResult & { address: string } | false>(false);

const hasOnlyBTCAccounts = accounts.every(({ coinCode }) => isBitcoinOnly(coinCode));

useEffect(() => {
if (config) {
setAgreedTerms(config.frontend.skipBitrefillWidgetDisclaimer);
}
}, [config]);

const onResize = useCallback(() => {
if (resizeTimerID.current) {
clearTimeout(resizeTimerID.current);
}
resizeTimerID.current = setTimeout(() => {
if (containerRef.current) {
setHeight(containerRef.current.offsetHeight);
}
}, 200);
}, []);

useEffect(() => {
onResize();
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
if (resizeTimerID.current) {
clearTimeout(resizeTimerID.current);
}
};
}, [onResize]);

const handleConfiguration = useCallback(async (event: MessageEvent) => {
if (
!account
Expand Down Expand Up @@ -254,8 +223,7 @@ export const Bitrefill = ({
sandbox="allow-same-origin allow-popups allow-scripts allow-forms"
src={bitrefillInfo.url}
onLoad={() => {
setIframeLoaded(true);
onResize();
onIframeLoad();
}}
/>
)}
Expand Down
43 changes: 7 additions & 36 deletions frontends/web/src/routes/market/btcdirect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

import { useState, useEffect, createRef, useContext, useRef, useCallback } from 'react';
import { useState, useEffect, useContext, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { getBTCDirectInfo, TMarketAction } from '@/api/market';
Expand All @@ -18,6 +18,7 @@ import { findAccount, isBitcoinOnly } from '@/routes/account/utils';
import { BTCDirectTerms } from '@/components/terms/btcdirect-terms';
import { MarketGuide } from './guide';
import { alertUser } from '@/components/alert/Alert';
import { useVendorIframeResizeHeight, useVendorTerms } from '@/hooks/vendor-iframe';
import style from './iframe.module.css';

// Map languages supported by BTC Direct
Expand Down Expand Up @@ -45,44 +46,15 @@ export const BTCDirect = ({
const { isDarkMode } = useDarkmode();
const navigate = useNavigate();

const iframeRef = useRef<HTMLIFrameElement | null>(null);
const btcdirectInfo = useLoad(() => getBTCDirectInfo(action, code));

const [agreedTerms, setAgreedTerms] = useState(false);
const [iframeLoaded, setIframeLoaded] = useState(false);
const [blocking, setBlocking] = useState(false);
const [height, setHeight] = useState(0);

const config = useLoad(getConfig);

const account = findAccount(accounts, code);
const ref = createRef<HTMLDivElement>();
let resizeTimerID: any;

useEffect(() => {
if (config) {
setAgreedTerms(config.frontend.skipBTCDirectWidgetDisclaimer);
}
}, [config]);

useEffect(() => {
onResize();
window.addEventListener('resize', onResize);

return () => window.removeEventListener('resize', onResize);
});

const onResize = () => {
if (resizeTimerID) {
clearTimeout(resizeTimerID);
}
resizeTimerID = setTimeout(() => {
if (!ref.current) {
return;
}
setHeight(ref.current.offsetHeight);
}, 200);
};
const { containerRef, height, iframeLoaded, iframeRef, onIframeLoad } = useVendorIframeResizeHeight();
const { agreedTerms, setAgreedTerms } = useVendorTerms(!!config?.frontend?.skipBTCDirectWidgetDisclaimer);

const handlePaymentRequest = useCallback(async (event: MessageEvent) => {
const {
Expand Down Expand Up @@ -216,7 +188,7 @@ export const BTCDirect = ({
useEffect(() => {
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
});
}, [onMessage]);

if (!account || !config) {
return null;
Expand All @@ -241,7 +213,7 @@ export const BTCDirect = ({
</h2>
} />
</div>
<div ref={ref} className={style.container}>
<div ref={containerRef} className={style.container}>
{ !agreedTerms ? (
<BTCDirectTerms
account={account}
Expand All @@ -257,8 +229,7 @@ export const BTCDirect = ({
{ btcdirectInfo?.success ? (
<iframe
onLoad={() => {
setIframeLoaded(true);
onResize();
onIframeLoad();
}}
ref={iframeRef}
title="BTC Direct"
Expand Down
Loading
Loading