Skip to content

Commit

Permalink
feat: ID-1619 Passport popup overlay (#1748)
Browse files Browse the repository at this point in the history
  • Loading branch information
imx-mikhala authored May 13, 2024
1 parent dbb0c3f commit 935a36e
Show file tree
Hide file tree
Showing 7 changed files with 591 additions and 21 deletions.
8 changes: 8 additions & 0 deletions packages/passport/sdk/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createConfig, MultiRollupAPIConfiguration, multiRollupConfig } from '@i
import {
OidcConfiguration,
PassportModuleConfiguration,
PopupOverlayOptions,
} from '../types';
import { PassportError, PassportErrorType } from '../errors/passportError';

Expand Down Expand Up @@ -49,10 +50,13 @@ export class PassportConfiguration {

readonly crossSdkBridgeEnabled: boolean;

readonly popupOverlayOptions: PopupOverlayOptions;

constructor({
baseConfig,
overrides,
crossSdkBridgeEnabled,
popupOverlayOptions,
...oidcConfiguration
}: PassportModuleConfiguration) {
validateConfiguration(oidcConfiguration, [
Expand All @@ -62,6 +66,10 @@ export class PassportConfiguration {
this.oidcConfiguration = oidcConfiguration;
this.baseConfig = baseConfig;
this.crossSdkBridgeEnabled = crossSdkBridgeEnabled || false;
this.popupOverlayOptions = popupOverlayOptions || {
disableGenericPopupOverlay: false,
disableBlockedPopupOverlay: false,
};

if (overrides) {
validateConfiguration(
Expand Down
115 changes: 94 additions & 21 deletions packages/passport/sdk/src/confirmation/confirmation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from './types';
import { openPopupCenter } from './popup';
import { PassportConfiguration } from '../config';
import Overlay from '../overlay/overlay';

const CONFIRMATION_WINDOW_TITLE = 'Confirm this transaction';
const CONFIRMATION_WINDOW_HEIGHT = 720;
Expand All @@ -23,8 +24,17 @@ export default class ConfirmationScreen {

private confirmationWindow: Window | undefined;

private popupOptions: { width: number; height: number } | undefined;

private overlay: Overlay | undefined;

private overlayClosed: boolean;

private timer: NodeJS.Timeout | undefined;

constructor(config: PassportConfiguration) {
this.config = config;
this.overlayClosed = false;
}

private getHref(relativePath: string, queryStringParams?: { [key: string]: any }) {
Expand Down Expand Up @@ -57,6 +67,7 @@ export default class ConfirmationScreen {
) {
return;
}

switch (data.messageType as ReceiveMessage) {
case ReceiveMessage.CONFIRMATION_WINDOW_READY: {
this.confirmationWindow?.postMessage({
Expand All @@ -66,26 +77,25 @@ export default class ConfirmationScreen {
break;
}
case ReceiveMessage.TRANSACTION_CONFIRMED: {
this.closeWindow();
resolve({ confirmed: true });
break;
}
case ReceiveMessage.TRANSACTION_ERROR: {
this.closeWindow();
reject(new Error('Error during transaction confirmation'));
break;
}
case ReceiveMessage.TRANSACTION_REJECTED: {
this.closeWindow();
reject(new Error('User rejected transaction'));
break;
}
default:
this.closeWindow();
reject(new Error('Unsupported message type'));
}
};
if (!this.confirmationWindow) {
resolve({ confirmed: false });
return;
}
window.addEventListener('message', messageHandler);

let href = '';
if (chainType === TransactionApprovalRequestChainTypeEnum.Starkex) {
Expand All @@ -95,6 +105,7 @@ export default class ConfirmationScreen {
transactionID: transactionId, etherAddress, chainType, chainID: chainId,
});
}
window.addEventListener('message', messageHandler);
this.showConfirmationScreen(href, messageHandler, resolve);
});
}
Expand All @@ -117,26 +128,26 @@ export default class ConfirmationScreen {
break;
}
case ReceiveMessage.MESSAGE_CONFIRMED: {
this.closeWindow();
resolve({ confirmed: true });
break;
}
case ReceiveMessage.MESSAGE_ERROR: {
this.closeWindow();
reject(new Error('Error during message confirmation'));
break;
}
case ReceiveMessage.MESSAGE_REJECTED: {
this.closeWindow();
reject(new Error('User rejected message'));
break;
}

default:
this.closeWindow();
reject(new Error('Unsupported message type'));
}
};
if (!this.confirmationWindow) {
resolve({ confirmed: false });
return;
}

window.addEventListener('message', messageHandler);
const href = this.getHref('zkevm/message', { messageID, etherAddress });
this.showConfirmationScreen(href, messageHandler, resolve);
Expand All @@ -149,27 +160,89 @@ export default class ConfirmationScreen {
return;
}

this.confirmationWindow = openPopupCenter({
url: this.getHref('loading'),
title: CONFIRMATION_WINDOW_TITLE,
width: popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
height: popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
});
this.popupOptions = popupOptions;

try {
this.confirmationWindow = openPopupCenter({
url: this.getHref('loading'),
title: CONFIRMATION_WINDOW_TITLE,
width: popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
height: popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
});
this.overlay = new Overlay(this.config.popupOverlayOptions);
} catch (e) {
// If an error is thrown here then the popup is blocked
this.overlay = new Overlay(this.config.popupOverlayOptions, true);
}

this.overlay.append(
() => {
try {
this.confirmationWindow?.close();
this.confirmationWindow = openPopupCenter({
url: this.getHref('loading'),
title: CONFIRMATION_WINDOW_TITLE,
width: this.popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
height: this.popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
});
} catch { /* Empty */ }
},
() => {
this.overlayClosed = true;
this.closeWindow();
},
);
}

closeWindow() {
this.confirmationWindow?.close();
this.overlay?.remove();
this.overlay = undefined;
}

showConfirmationScreen(href: string, messageHandler: MessageHandler, resolve: Function) {
this.confirmationWindow!.location.href = href;
// If popup blocked, the confirmation window will not exist
if (this.confirmationWindow) {
this.confirmationWindow.location.href = href;
}

// This indicates the user closed the overlay so the transaction should be rejected
if (!this.overlay) {
this.overlayClosed = false;
resolve({ confirmed: false });
return;
}

// https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
const timer = setInterval(() => {
if (this.confirmationWindow?.closed) {
clearInterval(timer);
const timerCallback = () => {
if (this.confirmationWindow?.closed || this.overlayClosed) {
clearInterval(this.timer);
window.removeEventListener('message', messageHandler);
resolve({ confirmed: false });
this.overlayClosed = false;
this.confirmationWindow = undefined;
}
}, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION);
};
this.timer = setInterval(
timerCallback,
CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION,
);
this.overlay.update(() => this.recreateConfirmationWindow(href, timerCallback));
}

private recreateConfirmationWindow(href: string, timerCallback: () => void) {
try {
// Clears and recreates the timer to ensure when the confirmation window
// is closed and recreated the transaction is not rejected.
clearInterval(this.timer);
this.confirmationWindow?.close();
this.confirmationWindow = openPopupCenter({
url: href,
title: CONFIRMATION_WINDOW_TITLE,
width: this.popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
height: this.popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
});
this.timer = setInterval(timerCallback, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION);
} catch { /* Empty */ }
}
}
Loading

0 comments on commit 935a36e

Please sign in to comment.