Skip to content
Open
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
35 changes: 29 additions & 6 deletions src/PeraWalletConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {isMobile} from "./util/device/deviceUtils";
import {AlgorandChainIDs} from "./util/peraWalletTypes";
import {runWebSignTransactionFlow} from "./util/sign/signTransactionFlow";
import {runWebConnectFlow} from "./util/connect/connectFlow";
import {getPublicSettings} from "./util/webview-api/webviewApi";

interface PeraWalletConnectOptions {
bridge?: string;
Expand All @@ -51,7 +52,8 @@ function generatePeraWalletConnectModalActions({
compactMode,
promoteMobile,
singleAccount,
selectedAccount
selectedAccount,
isInWebview
}: PeraWalletModalConfig) {
return {
open: openPeraWalletConnectModal({
Expand All @@ -61,7 +63,8 @@ function generatePeraWalletConnectModalActions({
compactMode,
promoteMobile,
singleAccount,
selectedAccount
selectedAccount,
isInWebview
}),
close: () => removeModalWrapperFromDOM(PERA_WALLET_CONNECT_MODAL_ID)
};
Expand All @@ -71,6 +74,7 @@ class PeraWalletConnect {
bridge: string;
connector: WalletConnect | null;
shouldShowSignTxnToast: boolean;
isInWebview: boolean;
chainId?: AlgorandChainIDs;
compactMode?: boolean;
singleAccount?: boolean;
Expand All @@ -85,6 +89,7 @@ class PeraWalletConnect {
: options.shouldShowSignTxnToast;

this.chainId = options?.chainId;
this.isInWebview = false;
this.compactMode = options?.compactMode || false;
this.singleAccount = options?.singleAccount || false;
}
Expand All @@ -107,6 +112,20 @@ class PeraWalletConnect {
return this.checkIsPeraDiscoverBrowser();
}

private async checkIsInWebview(): Promise<boolean> {
if (isMobile()) {
try {
const publicSettings = await getPublicSettings();

return publicSettings !== null;
} catch {
return false;
}
}

return false;
}

// `selectedAccount` option is only applicable for Pera Wallet products
connect(options?: {selectedAccount?: string}) {
return new Promise<string[]>(async (resolve, reject) => {
Expand All @@ -130,6 +149,8 @@ class PeraWalletConnect {
promoteMobile
} = await getPeraConnectConfig();

this.isInWebview = await this.checkIsInWebview();

const onWebWalletConnect = runWebConnectFlow({
resolve,
reject,
Expand All @@ -153,7 +174,8 @@ class PeraWalletConnect {
compactMode: this.compactMode,
promoteMobile,
singleAccount: this.singleAccount,
selectedAccount: options?.selectedAccount
selectedAccount: options?.selectedAccount,
isInWebview: this.isInWebview
})
});

Expand Down Expand Up @@ -230,6 +252,8 @@ class PeraWalletConnect {
}

// Pera Mobile Wallet flow
this.isInWebview = await this.checkIsInWebview();

if (this.connector) {
resolve(this.connector.accounts || []);
}
Expand Down Expand Up @@ -428,7 +452,7 @@ class PeraWalletConnect {
signerAddress?: string
): Promise<Uint8Array[]> {
if (this.platform === "mobile") {
if (isMobile()) {
if (isMobile() && !this.isInWebview) {
// This is to automatically open the wallet app when trying to sign with it.
openPeraWalletRedirectModal();
} else if (!isMobile() && this.shouldShowSignTxnToast) {
Expand Down Expand Up @@ -457,15 +481,14 @@ class PeraWalletConnect {

// Pera Mobile Wallet flow
return this.signTransactionWithMobile(signTxnRequestParams);
// ================================================= //
}

async signData(data: PeraWalletArbitraryData[], signer: string): Promise<Uint8Array[]> {
// eslint-disable-next-line no-magic-numbers
const chainId = this.chainId || 4160;

if (this.platform === "mobile") {
if (isMobile()) {
if (isMobile() && !this.isInWebview) {
// This is to automatically open the wallet app when trying to sign with it.
openPeraWalletRedirectModal();
} else if (!isMobile() && this.shouldShowSignTxnToast) {
Expand Down
102 changes: 56 additions & 46 deletions src/modal/PeraWalletConnectModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,67 +14,77 @@ export class PeraWalletConnectModal extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
}

connectedCallback() {
if (this.shadowRoot) {
const styleSheet = document.createElement("style");
this.render();
}
}

styleSheet.textContent = styles;
private render() {
if (!this.shadowRoot) {
return;
}

const isCompactMode = this.getAttribute("compact-mode") === "true";
const styleSheet = document.createElement("style");

if (isCompactMode) {
peraWalletConnectModalClassNames = `${peraWalletConnectModalClassNames} ${PERA_WALLET_MODAL_CLASSNAME}--compact`;
}
styleSheet.textContent = styles;

const singleAccount = this.getAttribute("single-account") === "true";
const selectedAccount = this.getAttribute("selected-account");
const isCompactMode = this.getAttribute("compact-mode") === "true";

if (isMobile()) {
peraWalletConnectModal.innerHTML = `
<div class="${peraWalletConnectModalClassNames}">
<div class="pera-wallet-modal__body" part="body">
<pera-wallet-modal-header modal-id="${PERA_WALLET_CONNECT_MODAL_ID}"></pera-wallet-modal-header/>

<pera-wallet-modal-touch-screen-mode uri="${this.getAttribute(
"uri"
)}" should-use-sound="${this.getAttribute(
"should-use-sound"
)}" single-account="${singleAccount}" selected-account="${selectedAccount}"></pera-wallet-modal-touch-screen-mode>
</div>
</div>
`;
if (isCompactMode) {
peraWalletConnectModalClassNames = `${peraWalletConnectModalClassNames} ${PERA_WALLET_MODAL_CLASSNAME}--compact`;
}

this.shadowRoot.append(
peraWalletConnectModal.content.cloneNode(true),
styleSheet
);
} else {
peraWalletConnectModal.innerHTML = `
const singleAccount = this.getAttribute("single-account") === "true";
const selectedAccount = this.getAttribute("selected-account") || undefined;

if (isMobile()) {
peraWalletConnectModal.innerHTML = `
<div class="${peraWalletConnectModalClassNames}">
<div class="pera-wallet-modal__body">
<div class="pera-wallet-modal__body" part="body">
<pera-wallet-modal-header modal-id="${PERA_WALLET_CONNECT_MODAL_ID}"></pera-wallet-modal-header/>

<pera-wallet-modal-desktop-mode id="pera-wallet-modal-desktop-mode" uri="${this.getAttribute(
"uri"
)}" is-web-wallet-avaliable="${this.getAttribute(
"is-web-wallet-avaliable"
)}" should-display-new-badge="${this.getAttribute(
"should-display-new-badge"
)}" compact-mode="${this.getAttribute(
"compact-mode"
)}" promote-mobile="${this.getAttribute(
"promote-mobile"
)}" single-account="${singleAccount}"
></pera-wallet-modal-desktop-mode>
<pera-wallet-modal-touch-screen-mode uri="${this.getAttribute(
"uri"
)}" should-use-sound="${this.getAttribute(
"should-use-sound"
)}" single-account="${singleAccount}" selected-account="${selectedAccount}"></pera-wallet-modal-touch-screen-mode>
</div>
</div>
`;

this.shadowRoot.append(
peraWalletConnectModal.content.cloneNode(true),
styleSheet
);
}
this.shadowRoot.append(
peraWalletConnectModal.content.cloneNode(true),
styleSheet
);
} else {
peraWalletConnectModal.innerHTML = `
<div class="${peraWalletConnectModalClassNames}">
<div class="pera-wallet-modal__body">
<pera-wallet-modal-header modal-id="${PERA_WALLET_CONNECT_MODAL_ID}"></pera-wallet-modal-header/>

<pera-wallet-modal-desktop-mode id="pera-wallet-modal-desktop-mode" uri="${this.getAttribute(
"uri"
)}" is-web-wallet-avaliable="${this.getAttribute(
"is-web-wallet-avaliable"
)}" should-display-new-badge="${this.getAttribute(
"should-display-new-badge"
)}" compact-mode="${this.getAttribute(
"compact-mode"
)}" promote-mobile="${this.getAttribute(
"promote-mobile"
)}" single-account="${singleAccount}"
></pera-wallet-modal-desktop-mode>
</div>
</div>
`;

this.shadowRoot.append(
peraWalletConnectModal.content.cloneNode(true),
styleSheet
);
}
}
}
8 changes: 4 additions & 4 deletions src/modal/_pera-wallet-modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@

&--mobile {
.pera-wallet-modal__body {
top: 40px;
position: absolute;
top: unset;
bottom: 0;
left: 0;

width: 100%;
max-width: unset;
height: calc(100 * var(--pera-wallet-vh));

padding: 20px;

Expand Down Expand Up @@ -190,13 +190,13 @@

@keyframes PeraWalletConnectMobileSlideIn {
0% {
top: 30%;
bottom: -30%;

opacity: 0;
}

100% {
top: 40px;
bottom: 0;

opacity: 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import QRCodeStyling from "qr-code-styling";

import styles from "./_pera-wallet-connect-modal-desktop-mode.scss";
import accordionStyles from "./accordion/_pera-wallet-accordion.scss";
// import {peraWalletFlowType} from "../../../util/device/deviceUtils";

const peraWalletConnectModalDesktopMode = document.createElement("template");
const styleSheet = document.createElement("style");
Expand Down
32 changes: 20 additions & 12 deletions src/modal/peraWalletConnectModalUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import PeraWalletConnectError from "../util/PeraWalletConnectError";
import {waitForElementCreatedAtShadowDOM} from "../util/dom/domUtils";
import {generatePeraWalletConnectDeepLink} from "../util/peraWalletUtils";

export type PERA_CONNECT_MODAL_VIEWS = "default" | "download-pera";

Expand All @@ -11,6 +12,7 @@ export interface PeraWalletModalConfig {
compactMode?: boolean;
singleAccount?: boolean;
selectedAccount?: string;
isInWebview?: boolean;
}

// The ID of the wrapper element for PeraWalletConnectModal
Expand Down Expand Up @@ -59,20 +61,26 @@ function createModalWrapperOnDOM(modalId: string) {
*/
function openPeraWalletConnectModal(modalConfig: PeraWalletModalConfig) {
return (uri: string) => {
if (!document.getElementById(PERA_WALLET_CONNECT_MODAL_ID)) {
const {
isWebWalletAvailable,
shouldDisplayNewBadge,
shouldUseSound,
compactMode,
promoteMobile,
singleAccount,
selectedAccount,
isInWebview
} = modalConfig;

if (isInWebview) {
const deepLink = generatePeraWalletConnectDeepLink(uri, {singleAccount, selectedAccount});

window.open(deepLink, "_blank");
} else if (!document.getElementById(PERA_WALLET_CONNECT_MODAL_ID)) {
const root = createModalWrapperOnDOM(PERA_WALLET_CONNECT_MODAL_ID);
const newURI = `${uri}&algorand=true`;
const {
isWebWalletAvailable,
shouldDisplayNewBadge,
shouldUseSound,
compactMode,
promoteMobile,
singleAccount,
selectedAccount
} = modalConfig;

root.innerHTML = `<pera-wallet-connect-modal uri="${newURI}" is-web-wallet-avaliable="${isWebWalletAvailable}" should-display-new-badge="${shouldDisplayNewBadge}" should-use-sound="${shouldUseSound}" compact-mode="${compactMode}" promote-mobile="${promoteMobile}" single-account="${singleAccount}" selected-account="${selectedAccount || ''}"></pera-wallet-connect-modal>`;

root.innerHTML = `<pera-wallet-connect-modal uri="${newURI}" is-web-wallet-avaliable="${isWebWalletAvailable}" should-display-new-badge="${shouldDisplayNewBadge}" should-use-sound="${shouldUseSound}" compact-mode="${compactMode}" promote-mobile="${promoteMobile}" single-account="${singleAccount}" selected-account="${selectedAccount || ''}" is-in-webview="${isInWebview || false}"></pera-wallet-connect-modal>`;
}
};
}
Expand Down
11 changes: 9 additions & 2 deletions src/util/screen/setDynamicVhValue.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import {setVhVariable} from "./screenSizeUtils";

(function setDynamicVhValue() {
window.addEventListener("DOMContentLoaded", () => {
// Call immediately if DOM is already loaded
if (document.readyState === "complete" || document.readyState === "interactive") {
setVhVariable();
});
} else {
// Otherwise wait for DOMContentLoaded
window.addEventListener("DOMContentLoaded", () => {
setVhVariable();
});
}

// Always set up resize listener
window.addEventListener("resize", () => {
setVhVariable();
});
Expand Down
16 changes: 16 additions & 0 deletions src/util/webview-api/webviewApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Webview API Implementation
* Pera Connect WebView API functions
*/
import { callMobileMethodWithResponse } from "./webviewBridge";
import type { PublicSettings } from "./webviewApiTypes";

const DEFAULT_TIMEOUT = 2000;

/**
* Get public settings
* Returns privacy-safe subset of settings (for Pera Connect)
*/
export function getPublicSettings(timeoutMs = DEFAULT_TIMEOUT): Promise<PublicSettings | null> {
return callMobileMethodWithResponse<PublicSettings | null>("getPublicSettings", timeoutMs);
}
Loading