From b0c9041a1fe2ff6d37291395696fa3cbeb2ad518 Mon Sep 17 00:00:00 2001 From: cb-jake <95890768+cb-jake@users.noreply.github.com> Date: Thu, 10 Nov 2022 16:02:48 -0700 Subject: [PATCH] Wallet SDK UI update (#682) * add setAppSrc to ui and appSrc to relay * clean up var name * Add new connect dialog and update tsconfig+ components * copy over last UI updates * move components to folders * update styles * move spinner into dir * move snackbar into dir * style updates * move linkflow into dir * fix tests * add darkmode support * style fixup * fix lint * re-enable test * clean up * fixup connect content for darkmode * update darkmode for try extension content * lint/prettier * add test logs * fix setting app src * prettier * update key * add upgrade message * add jm's url update * remove whitespace * prettier --- packages/wallet-sdk/package.json | 1 + packages/wallet-sdk/src/CoinbaseWalletSDK.ts | 5 +- .../ConnectContent/ConnectContent.scss | 242 +++++++++++ .../ConnectContent/ConnectContent.tsx | 271 +++++++++++++ .../ConnectDialog/ConnectDialog.scss | 64 +++ .../ConnectDialog.test.tsx} | 20 +- .../ConnectDialog/ConnectDialog.tsx | 95 +++++ .../{ => LinkFlow}/LinkFlow.test.tsx | 0 .../components/{ => LinkFlow}/LinkFlow.tsx | 4 +- .../components/{ => Snackbar}/Snackbar.scss | 0 .../{ => Snackbar}/Snackbar.test.tsx | 1 + .../components/{ => Snackbar}/Snackbar.tsx | 18 +- .../{ => Snackbar}/SnackbarContainer.test.tsx | 0 .../src/components/{ => Spinner}/Spinner.scss | 1 - .../components/{ => Spinner}/Spinner.test.tsx | 0 .../src/components/{ => Spinner}/Spinner.tsx | 0 .../TryExtensionContent.scss | 140 +++++++ .../TryExtensionContent.tsx | 88 ++++ .../components/TryExtensionLinkDialog.scss | 380 ------------------ .../src/components/TryExtensionLinkDialog.tsx | 271 ------------- .../src/components/icons/ArrowLeftIcon.tsx | 15 + .../src/components/icons/CloseIcon.tsx | 16 + .../src/components/icons/LaptopIcon.tsx | 16 + .../src/components/icons/QRCodeIcon.tsx | 23 ++ .../wallet-sdk/src/components/icons/QRLogo.ts | 8 - .../src/components/icons/QRLogoCoinbase.tsx | 9 + .../src/components/icons/QRLogoWallet.ts | 8 + .../src/components/icons/SafeIcon.tsx | 19 + .../src/components/icons/StatusDotIcon.tsx | 19 + .../src/components/icons/coinbase-round.svg | 4 + .../icons/coinbase-wallet-round.svg | 5 + .../src/components/icons/coinbase.svg | 20 + .../src/components/icons/globe-icon.svg | 3 - .../src/components/icons/link-icon.svg | 4 - .../src/components/icons/lock-icon.svg | 4 - packages/wallet-sdk/src/components/theme.scss | 16 + packages/wallet-sdk/src/components/types.ts | 1 + packages/wallet-sdk/src/lib/cssReset.scss | 17 +- .../src/provider/WalletSDKUI.test.ts | 6 +- .../wallet-sdk/src/provider/WalletSDKUI.ts | 15 +- packages/wallet-sdk/src/provider/WalletUI.ts | 5 + .../wallet-sdk/src/relay/WalletSDKRelay.ts | 21 +- 42 files changed, 1149 insertions(+), 706 deletions(-) create mode 100644 packages/wallet-sdk/src/components/ConnectContent/ConnectContent.scss create mode 100644 packages/wallet-sdk/src/components/ConnectContent/ConnectContent.tsx create mode 100644 packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.scss rename packages/wallet-sdk/src/components/{TryExtensionLinkDialog.test.tsx => ConnectDialog/ConnectDialog.test.tsx} (78%) create mode 100644 packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.tsx rename packages/wallet-sdk/src/components/{ => LinkFlow}/LinkFlow.test.tsx (100%) rename packages/wallet-sdk/src/components/{ => LinkFlow}/LinkFlow.tsx (97%) rename packages/wallet-sdk/src/components/{ => Snackbar}/Snackbar.scss (100%) rename packages/wallet-sdk/src/components/{ => Snackbar}/Snackbar.test.tsx (98%) rename packages/wallet-sdk/src/components/{ => Snackbar}/Snackbar.tsx (63%) rename packages/wallet-sdk/src/components/{ => Snackbar}/SnackbarContainer.test.tsx (100%) rename packages/wallet-sdk/src/components/{ => Spinner}/Spinner.scss (99%) rename packages/wallet-sdk/src/components/{ => Spinner}/Spinner.test.tsx (100%) rename packages/wallet-sdk/src/components/{ => Spinner}/Spinner.tsx (100%) create mode 100644 packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.scss create mode 100644 packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.tsx delete mode 100644 packages/wallet-sdk/src/components/TryExtensionLinkDialog.scss delete mode 100644 packages/wallet-sdk/src/components/TryExtensionLinkDialog.tsx create mode 100644 packages/wallet-sdk/src/components/icons/ArrowLeftIcon.tsx create mode 100644 packages/wallet-sdk/src/components/icons/CloseIcon.tsx create mode 100644 packages/wallet-sdk/src/components/icons/LaptopIcon.tsx create mode 100644 packages/wallet-sdk/src/components/icons/QRCodeIcon.tsx delete mode 100644 packages/wallet-sdk/src/components/icons/QRLogo.ts create mode 100644 packages/wallet-sdk/src/components/icons/QRLogoCoinbase.tsx create mode 100644 packages/wallet-sdk/src/components/icons/QRLogoWallet.ts create mode 100644 packages/wallet-sdk/src/components/icons/SafeIcon.tsx create mode 100644 packages/wallet-sdk/src/components/icons/StatusDotIcon.tsx create mode 100644 packages/wallet-sdk/src/components/icons/coinbase-round.svg create mode 100644 packages/wallet-sdk/src/components/icons/coinbase-wallet-round.svg create mode 100644 packages/wallet-sdk/src/components/icons/coinbase.svg delete mode 100644 packages/wallet-sdk/src/components/icons/globe-icon.svg delete mode 100644 packages/wallet-sdk/src/components/icons/link-icon.svg delete mode 100644 packages/wallet-sdk/src/components/icons/lock-icon.svg create mode 100644 packages/wallet-sdk/src/components/theme.scss create mode 100644 packages/wallet-sdk/src/components/types.ts diff --git a/packages/wallet-sdk/package.json b/packages/wallet-sdk/package.json index fc8ea692a4..3b2d74092e 100644 --- a/packages/wallet-sdk/package.json +++ b/packages/wallet-sdk/package.json @@ -36,6 +36,7 @@ "lint:types": "tsc --noEmit", "lint:prettier": "prettier --check \"{src,__tests__}/**/*.(js|ts|tsx)\"", "lint:eslint": "eslint ./src --ext .ts,.tsx", + "lint": "yarn lint:eslint && yarn lint:types && yarn lint:prettier", "fix:eslint": "yarn lint:eslint --fix", "fix:prettier": "prettier . --write", "release": "./scripts/release.sh" diff --git a/packages/wallet-sdk/src/CoinbaseWalletSDK.ts b/packages/wallet-sdk/src/CoinbaseWalletSDK.ts index 1f57c62dc7..b38b51a6c5 100644 --- a/packages/wallet-sdk/src/CoinbaseWalletSDK.ts +++ b/packages/wallet-sdk/src/CoinbaseWalletSDK.ts @@ -99,10 +99,9 @@ export class CoinbaseWalletSDK { this._reloadOnDisconnect = options.reloadOnDisconnect ?? true; - const u = new URL(linkAPIUrl); - const origin = `${u.protocol}//${u.host}`; + const url = new URL(linkAPIUrl); + const origin = `${url.protocol}//${url.host}`; this._storage = new ScopedLocalStorage(`-walletlink:${origin}`); // needs migration to preserve local states - this._storage.setItem("version", CoinbaseWalletSDK.VERSION); if (this.walletExtension || this.coinbaseBrowser) { diff --git a/packages/wallet-sdk/src/components/ConnectContent/ConnectContent.scss b/packages/wallet-sdk/src/components/ConnectContent/ConnectContent.scss new file mode 100644 index 0000000000..9e1c93d4b9 --- /dev/null +++ b/packages/wallet-sdk/src/components/ConnectContent/ConnectContent.scss @@ -0,0 +1,242 @@ +// Copyright (c) 2018-2022 Coinbase, Inc. +// Licensed under the Apache License, version 2.0 + +@import "../theme.scss"; + +.-cbwsdk-css-reset { + .-cbwsdk-connect-content { + height: 430px; + width: 700px; + border-radius: 12px; + padding: 30px; + + &.light { + background: $white; + } + + &.dark { + background: $black; + } + + &-header { + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 0 30px; + } + + &-heading { + font-style: normal; + font-weight: 500; + font-size: 28px; + line-height: 36px; + margin: 0; + + &.light { + color: $black; + } + + &.dark { + color: $white; + } + } + + &-layout { + display: flex; + flex-direction: row; + } + + &-column-left { + margin-right: 30px; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + &-column-right { + flex: 25%; + margin-right: 34px; + } + + &-qr-wrapper { + width: 220px; + height: 220px; + border-radius: 12px; + display: flex; + justify-content: center; + align-items: center; + background: $white; + } + + &-qr-connecting { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + &.light { + background-color: rgba($white, 0.95); + + > p { + color: $black; + } + } + + &.dark { + background-color: rgba($background-dark, 0.9); + + > p { + color: $white; + } + } + + > p { + font-size: 12px; + font-weight: bold; + margin-top: 16px; + } + } + + &-update-app { + border-radius: 8px; + font-size: 14px; + line-height: 20px; + padding: 12px; + width: 339px; + + &.light { + background: $background-alt; + color: $foreground-muted; + } + + &.dark { + background: $background-alt-dark; + color: $foreground-muted-dark; + } + } + } + + .-cbwsdk-cancel-button { + -webkit-appearance: none; + border: none; + background: none; + cursor: pointer; + padding: 0; + margin: 0; + + &-x { + position: relative; + display: block; + cursor: pointer; + } + } + + .-cbwsdk-wallet-steps { + padding: 0 0 0 16px; + margin: 0; + width: 100%; + list-style: decimal; + + &-item { + list-style-type: decimal; + display: list-item; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 24px; + margin-top: 20px; + + &.light { + color: $black; + } + + &.dark { + color: $white; + } + } + + &-item-wrapper { + display: flex; + align-items: center; + } + + &-pad-left { + margin-left: 6px; + } + + &-icon { + display: flex; + border-radius: 50%; + height: 24px; + width: 24px; + + svg { + margin: auto; + display: block; + } + + &.light { + background: $primary; + } + + &.dark { + background: $primary-dark; + } + } + } + + .-cbwsdk-connect-item { + align-items: center; + display: flex; + flex-direction: row; + padding: 16px 24px; + gap: 12px; + cursor: pointer; + + &.light { + color: $black; + + &.selected { + background: $primary-wash; + color: $primary; + } + } + + &.dark { + color: $white; + + &.selected { + background: $primary-wash-dark; + color: $primary-dark; + } + } + + &.selected { + border-radius: 100px; + font-weight: 600; + } + + &-copy-wrapper { + margin: 0 4px 0 8px; + } + + &-title { + margin: 0 0 0; + font-size: 16px; + line-height: 24px; + font-weight: 500; + } + + &-description { + font-weight: 400; + font-size: 14px; + line-height: 20px; + margin: 0; + } + } +} diff --git a/packages/wallet-sdk/src/components/ConnectContent/ConnectContent.tsx b/packages/wallet-sdk/src/components/ConnectContent/ConnectContent.tsx new file mode 100644 index 0000000000..d798575e7b --- /dev/null +++ b/packages/wallet-sdk/src/components/ConnectContent/ConnectContent.tsx @@ -0,0 +1,271 @@ +// Copyright (c) 2018-2022 Coinbase, Inc. +// Licensed under the Apache License, version 2.0 + +import clsx from "clsx"; +import { h } from "preact"; +import { useCallback, useState } from "preact/hooks"; + +import { createQrUrl } from "../../util"; +import { LIB_VERSION } from "../../version"; +import { CloseIcon } from "../icons/CloseIcon"; +import coinbaseRound from "../icons/coinbase-round-svg"; +import coinbaseWalletRound from "../icons/coinbase-wallet-round-svg"; +import { QRCodeIcon } from "../icons/QRCodeIcon"; +import coinbaseLogo from "../icons/QRLogoCoinbase"; +import walletLogo from "../icons/QRLogoWallet"; +import { StatusDotIcon } from "../icons/StatusDotIcon"; +import { QRCode } from "../QRCode"; +import { Spinner } from "../Spinner/Spinner"; +import { Theme } from "../types"; +import css from "./ConnectContent-css"; + +type ConnectContentProps = { + theme: Theme; + version: string; + sessionId: string; + sessionSecret: string; + linkAPIUrl: string; + isConnected: boolean; + isParentConnection: boolean; + chainId: number; + onCancel: (() => void) | null; +}; + +const wallets = { + "coinbase-wallet-app": { + title: "Coinbase Wallet app", + description: "Connect with your self-custody wallet", + icon: coinbaseWalletRound, + steps: CoinbaseWalletSteps, + }, + "coinbase-app": { + title: "Coinbase app", + description: "Connect with your Coinbase account", + icon: coinbaseRound, + steps: CoinbaseAppSteps, + }, +}; + +type WalletType = keyof typeof wallets; + +const makeQrCodeImage = (app: string) => { + switch (app) { + case "coinbase-app": + return coinbaseLogo; + case "coinbase-wallet-app": + default: + return walletLogo; + } +}; + +const makeIconColor = (theme: Theme) => { + return theme === "light" ? "#FFFFFF" : "#0A0B0D"; +}; + +export function ConnectContent(props: ConnectContentProps) { + const { theme } = props; + const [selected, setSelected] = useState("coinbase-wallet-app"); + + const handleSelect = useCallback((id: WalletType) => { + setSelected(id); + }, []); + + const qrUrl = createQrUrl( + props.sessionId, + props.sessionSecret, + props.linkAPIUrl, + props.isParentConnection, + props.version, + props.chainId, + ); + + const wallet = wallets[selected]; + if (!selected) { + return null; + } + const WalletSteps = wallet.steps; + const coinbaseApp = selected === "coinbase-app"; + + return ( +
+ +
+

+ Scan to connect with one of our mobile apps +

+ {props.onCancel && ( + + )} +
+
+
+
+ {Object.entries(wallets).map(([key, value]) => { + return ( + handleSelect(key as WalletType)} + theme={theme} + /> + ); + })} +
+ {coinbaseApp && ( +
+ Don’t see a Scan option? Update your Coinbase app + to the latest version and try again. +
+ )} +
+
+
+ + + +
+ + {!props.isConnected && ( +
+ +

Connecting...

+
+ )} +
+
+
+ ); +} + +type ConnectItemProps = { + title: string; + description: string; + icon: string; + selected: boolean; + onClick(): void; + theme: Theme; +}; + +export function ConnectItem({ + title, + description, + icon, + selected, + theme, + onClick, +}: ConnectItemProps) { + return ( +
+
+ {title} +
+
+

{title}

+

{description}

+
+
+ ); +} + +type WalletStepsProps = { + theme: Theme; +}; + +export function CoinbaseWalletSteps({ theme }: WalletStepsProps) { + return ( +
    +
  1. +
    + Open Coinbase Wallet app +
    +
  2. +
  3. +
    + + Tap Scan{" "} + + + + +
    +
  4. +
+ ); +} + +export function CoinbaseAppSteps({ theme }: WalletStepsProps) { + return ( +
    +
  1. +
    Open Coinbase app
    +
  2. +
  3. +
    + + Tap More + + + + + + then Scan + + + + +
    +
  4. +
+ ); +} diff --git a/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.scss b/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.scss new file mode 100644 index 0000000000..4fdd0aaa60 --- /dev/null +++ b/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.scss @@ -0,0 +1,64 @@ +// Copyright (c) 2018-2022 Coinbase, Inc. +// Licensed under the Apache License, version 2.0 + +@import "../theme.scss"; + +.-cbwsdk-css-reset { + .-cbwsdk-connect-dialog { + z-index: 2147483647; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + + $white: #fff; + $black: #000; + + &-backdrop { + z-index: 2147483647; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + transition: opacity 0.25s; + + &.light { + background-color: rgba($black, 0.5); + } + + &.dark { + background-color: rgba(50, 53, 61, 0.4); + } + + &-hidden { + opacity: 0; + } + } + + &-box { + display: flex; + position: relative; + flex-direction: column; + transform: scale(1); + transition: opacity 0.25s, transform 0.25s; + + &-hidden { + opacity: 0; + transform: scale(0.85); + } + } + + &-container { + display: block; + + &-hidden { + display: none; + } + } + } +} diff --git a/packages/wallet-sdk/src/components/TryExtensionLinkDialog.test.tsx b/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.test.tsx similarity index 78% rename from packages/wallet-sdk/src/components/TryExtensionLinkDialog.test.tsx rename to packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.test.tsx index f50f080949..47790e47fe 100644 --- a/packages/wallet-sdk/src/components/TryExtensionLinkDialog.test.tsx +++ b/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.test.tsx @@ -1,14 +1,14 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/preact"; import { h } from "preact"; -import { TryExtensionLinkDialog } from "./TryExtensionLinkDialog"; +import { ConnectDialog } from "./ConnectDialog"; -const renderTryExtensionLinkDialog = ({ +const renderConnectDialog = ({ connectDisabled = false, isConnected = true, }) => { return render( - { test("should show scan QR box when connectDisabled is false", async () => { - renderTryExtensionLinkDialog({ connectDisabled: false }); + renderConnectDialog({ connectDisabled: false }); await waitFor(() => { - expect(screen.queryByTestId("scan-qr-box")).toBeTruthy(); + expect(screen.queryByTestId("connect-content")).toBeTruthy(); }); }); test("should not show scan QR box when connectDisabled is true", async () => { - renderTryExtensionLinkDialog({ connectDisabled: true }); + renderConnectDialog({ connectDisabled: true }); await waitFor(() => { - expect(screen.queryByTestId("scan-qr-box")).toBeNull(); + expect(screen.queryByTestId("connect-content")).toBeNull(); }); }); test("should show connecting spinner when not connected", async () => { - renderTryExtensionLinkDialog({ isConnected: false }); + renderConnectDialog({ isConnected: false }); await waitFor(() => { expect(screen.queryByTestId("connecting-spinner")).toBeTruthy(); @@ -55,7 +55,7 @@ describe("TryExtensionLinkDialog", () => { const mockedWindowOpen = jest.fn(); windowOpenSpy.mockImplementation(mockedWindowOpen); - renderTryExtensionLinkDialog({}); + renderConnectDialog({}); await waitFor(async () => { const button = await screen.findByRole("button", { name: "Install" }); @@ -70,7 +70,7 @@ describe("TryExtensionLinkDialog", () => { test("should show refresh button after pressing install", async () => { windowOpenSpy.mockImplementation(() => null); - renderTryExtensionLinkDialog({}); + renderConnectDialog({}); await waitFor(async () => { const button = await screen.findByRole("button", { name: "Install" }); diff --git a/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.tsx b/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.tsx new file mode 100644 index 0000000000..1912478185 --- /dev/null +++ b/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.tsx @@ -0,0 +1,95 @@ +// Copyright (c) 2018-2022 Coinbase, Inc. +// Licensed under the Apache License, version 2.0 + +import clsx from "clsx"; +import { h } from "preact"; +import { useEffect, useState } from "preact/hooks"; + +import { ConnectContent } from "../ConnectContent/ConnectContent"; +import { TryExtensionContent } from "../TryExtensionContent/TryExtensionContent"; +import css from "./ConnectDialog-css"; + +type ConnectDialogProps = { + darkMode: boolean; + version: string; + sessionId: string; + sessionSecret: string; + linkAPIUrl: string; + isOpen: boolean; + isConnected: boolean; + isParentConnection: boolean; + chainId: number; + connectDisabled: boolean; + onCancel: (() => void) | null; +}; + +export const ConnectDialog = (props: ConnectDialogProps) => { + const { isOpen, darkMode } = props; + const [containerHidden, setContainerHidden] = useState(!isOpen); + const [dialogHidden, setDialogHidden] = useState(!isOpen); + + useEffect(() => { + const timers = [ + window.setTimeout(() => { + setDialogHidden(!isOpen); + }, 10), + ]; + + if (isOpen) { + setContainerHidden(false); + } else { + timers.push( + window.setTimeout(() => { + setContainerHidden(true); + }, 360), + ); + } + + return () => { + timers.forEach(window.clearTimeout); + }; + }, [props.isOpen]); + + const theme = darkMode ? "dark" : "light"; + + return ( +
+ +
+
+
+ {!props.connectDisabled ? ( + + ) : null} + +
+
+
+ ); +}; diff --git a/packages/wallet-sdk/src/components/LinkFlow.test.tsx b/packages/wallet-sdk/src/components/LinkFlow/LinkFlow.test.tsx similarity index 100% rename from packages/wallet-sdk/src/components/LinkFlow.test.tsx rename to packages/wallet-sdk/src/components/LinkFlow/LinkFlow.test.tsx diff --git a/packages/wallet-sdk/src/components/LinkFlow.tsx b/packages/wallet-sdk/src/components/LinkFlow/LinkFlow.tsx similarity index 97% rename from packages/wallet-sdk/src/components/LinkFlow.tsx rename to packages/wallet-sdk/src/components/LinkFlow/LinkFlow.tsx index 40b7633084..746fe96592 100644 --- a/packages/wallet-sdk/src/components/LinkFlow.tsx +++ b/packages/wallet-sdk/src/components/LinkFlow/LinkFlow.tsx @@ -4,7 +4,7 @@ import { h, render } from "preact"; import { BehaviorSubject, Observable, Subject, Subscription } from "rxjs"; -import { TryExtensionLinkDialog } from "./TryExtensionLinkDialog"; +import { ConnectDialog } from "../ConnectDialog/ConnectDialog"; export interface LinkFlowOptions { darkMode: boolean; @@ -117,7 +117,7 @@ export class LinkFlow { } render( - { test("@presentItem", async () => { snackbar.presentItem({ message: "Confirm on phone", + appSrc: "coinbase-wallet", menuItems: [ { isRed: true, diff --git a/packages/wallet-sdk/src/components/Snackbar.tsx b/packages/wallet-sdk/src/components/Snackbar/Snackbar.tsx similarity index 63% rename from packages/wallet-sdk/src/components/Snackbar.tsx rename to packages/wallet-sdk/src/components/Snackbar/Snackbar.tsx index 83d5c4ab21..94ea39cac8 100644 --- a/packages/wallet-sdk/src/components/Snackbar.tsx +++ b/packages/wallet-sdk/src/components/Snackbar/Snackbar.tsx @@ -7,9 +7,18 @@ import { useEffect, useState } from "preact/hooks"; import css from "./Snackbar-css"; -const cblogo = `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEuNDkyIDEwLjQxOWE4LjkzIDguOTMgMCAwMTguOTMtOC45M2gxMS4xNjNhOC45MyA4LjkzIDAgMDE4LjkzIDguOTN2MTEuMTYzYTguOTMgOC45MyAwIDAxLTguOTMgOC45M0gxMC40MjJhOC45MyA4LjkzIDAgMDEtOC45My04LjkzVjEwLjQxOXoiIGZpbGw9IiMxNjUyRjAiLz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTEwLjQxOSAwSDIxLjU4QzI3LjMzNSAwIDMyIDQuNjY1IDMyIDEwLjQxOVYyMS41OEMzMiAyNy4zMzUgMjcuMzM1IDMyIDIxLjU4MSAzMkgxMC40MkM0LjY2NSAzMiAwIDI3LjMzNSAwIDIxLjU4MVYxMC40MkMwIDQuNjY1IDQuNjY1IDAgMTAuNDE5IDB6bTAgMS40ODhhOC45MyA4LjkzIDAgMDAtOC45MyA4LjkzdjExLjE2M2E4LjkzIDguOTMgMCAwMDguOTMgOC45M0gyMS41OGE4LjkzIDguOTMgMCAwMDguOTMtOC45M1YxMC40MmE4LjkzIDguOTMgMCAwMC04LjkzLTguOTNIMTAuNDJ6IiBmaWxsPSIjZmZmIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNS45OTggMjYuMDQ5Yy01LjU0OSAwLTEwLjA0Ny00LjQ5OC0xMC4wNDctMTAuMDQ3IDAtNS41NDggNC40OTgtMTAuMDQ2IDEwLjA0Ny0xMC4wNDYgNS41NDggMCAxMC4wNDYgNC40OTggMTAuMDQ2IDEwLjA0NiAwIDUuNTQ5LTQuNDk4IDEwLjA0Ny0xMC4wNDYgMTAuMDQ3eiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMi43NjIgMTQuMjU0YzAtLjgyMi42NjctMS40ODkgMS40ODktMS40ODloMy40OTdjLjgyMiAwIDEuNDg4LjY2NiAxLjQ4OCAxLjQ4OXYzLjQ5N2MwIC44MjItLjY2NiAxLjQ4OC0xLjQ4OCAxLjQ4OGgtMy40OTdhMS40ODggMS40ODggMCAwMS0xLjQ4OS0xLjQ4OHYtMy40OTh6IiBmaWxsPSIjMTY1MkYwIi8+PC9zdmc+`; const gearIcon = `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDYuNzV2LTEuNWwtMS43Mi0uNTdjLS4wOC0uMjctLjE5LS41Mi0uMzItLjc3bC44MS0xLjYyLTEuMDYtMS4wNi0xLjYyLjgxYy0uMjQtLjEzLS41LS4yNC0uNzctLjMyTDYuNzUgMGgtMS41bC0uNTcgMS43MmMtLjI3LjA4LS41My4xOS0uNzcuMzJsLTEuNjItLjgxLTEuMDYgMS4wNi44MSAxLjYyYy0uMTMuMjQtLjI0LjUtLjMyLjc3TDAgNS4yNXYxLjVsMS43Mi41N2MuMDguMjcuMTkuNTMuMzIuNzdsLS44MSAxLjYyIDEuMDYgMS4wNiAxLjYyLS44MWMuMjQuMTMuNS4yMy43Ny4zMkw1LjI1IDEyaDEuNWwuNTctMS43MmMuMjctLjA4LjUyLS4xOS43Ny0uMzJsMS42Mi44MSAxLjA2LTEuMDYtLjgxLTEuNjJjLjEzLS4yNC4yMy0uNS4zMi0uNzdMMTIgNi43NXpNNiA4LjVhMi41IDIuNSAwIDAxMC01IDIuNSAyLjUgMCAwMTAgNXoiIGZpbGw9IiMwNTBGMTkiLz48L3N2Zz4=`; +function makeSnackbarIcon(appSrc?: string | null) { + switch (appSrc) { + case "coinbase-app": + return `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE0LjY3NCAxOC44NThjLTIuMDQ1IDAtMy42NDgtMS43MjItMy42NDgtMy44NDVzMS42NTktMy44NDUgMy42NDgtMy44NDVjMS44MjQgMCAzLjMxNyAxLjM3NyAzLjU5MyAzLjIxNGgzLjcwM2MtLjMzMS0zLjk2LTMuNDgyLTcuMDU5LTcuMjk2LTcuMDU5LTQuMDM0IDAtNy4zNSAzLjQ0My03LjM1IDcuNjkgMCA0LjI0NiAzLjI2IDcuNjkgNy4zNSA3LjY5IDMuODcgMCA2Ljk2NS0zLjEgNy4yOTYtNy4wNTloLTMuNzAzYy0uMjc2IDEuODM2LTEuNzY5IDMuMjE0LTMuNTkzIDMuMjE0WiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0wIDEwLjY3OGMwLTMuNzExIDAtNS41OTYuNzQyLTcuMDIzQTYuNTMyIDYuNTMyIDAgMCAxIDMuNjU1Ljc0MkM1LjA4MiAwIDYuOTY3IDAgMTAuNjc4IDBoNy45MzhjMy43MTEgMCA1LjU5NiAwIDcuMDIzLjc0MmE2LjUzMSA2LjUzMSAwIDAgMSAyLjkxMyAyLjkxM2MuNzQyIDEuNDI3Ljc0MiAzLjMxMi43NDIgNy4wMjN2Ny45MzhjMCAzLjcxMSAwIDUuNTk2LS43NDIgNy4wMjNhNi41MzEgNi41MzEgMCAwIDEtMi45MTMgMi45MTNjLTEuNDI3Ljc0Mi0zLjMxMi43NDItNy4wMjMuNzQyaC03LjkzOGMtMy43MTEgMC01LjU5NiAwLTcuMDIzLS43NDJhNi41MzEgNi41MzEgMCAwIDEtMi45MTMtMi45MTNDMCAyNC4yMTIgMCAyMi4zODQgMCAxOC42MTZ2LTcuOTM4WiIgZmlsbD0iIzAwNTJGRiIvPjxwYXRoIGQ9Ik0xNC42ODQgMTkuNzczYy0yLjcyNyAwLTQuODY0LTIuMjk1LTQuODY0LTUuMTI2IDAtMi44MzEgMi4yMS01LjEyNyA0Ljg2NC01LjEyNyAyLjQzMiAwIDQuNDIyIDEuODM3IDQuNzkgNC4yODVoNC45MzhjLS40NDItNS4yOC00LjY0My05LjQxMS05LjcyOC05LjQxMS01LjM4IDAtOS44MDIgNC41OS05LjgwMiAxMC4yNTMgMCA1LjY2MiA0LjM0OCAxMC4yNTMgOS44MDIgMTAuMjUzIDUuMTU5IDAgOS4yODYtNC4xMzIgOS43MjgtOS40MTFoLTQuOTM4Yy0uMzY4IDIuNDQ4LTIuMzU4IDQuMjg0LTQuNzkgNC4yODRaIiBmaWxsPSIjZmZmIi8+PC9zdmc+`; + case "coinbase-wallet-app": + default: + return `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEuNDkyIDEwLjQxOWE4LjkzIDguOTMgMCAwMTguOTMtOC45M2gxMS4xNjNhOC45MyA4LjkzIDAgMDE4LjkzIDguOTN2MTEuMTYzYTguOTMgOC45MyAwIDAxLTguOTMgOC45M0gxMC40MjJhOC45MyA4LjkzIDAgMDEtOC45My04LjkzVjEwLjQxOXoiIGZpbGw9IiMxNjUyRjAiLz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTEwLjQxOSAwSDIxLjU4QzI3LjMzNSAwIDMyIDQuNjY1IDMyIDEwLjQxOVYyMS41OEMzMiAyNy4zMzUgMjcuMzM1IDMyIDIxLjU4MSAzMkgxMC40MkM0LjY2NSAzMiAwIDI3LjMzNSAwIDIxLjU4MVYxMC40MkMwIDQuNjY1IDQuNjY1IDAgMTAuNDE5IDB6bTAgMS40ODhhOC45MyA4LjkzIDAgMDAtOC45MyA4LjkzdjExLjE2M2E4LjkzIDguOTMgMCAwMDguOTMgOC45M0gyMS41OGE4LjkzIDguOTMgMCAwMDguOTMtOC45M1YxMC40MmE4LjkzIDguOTMgMCAwMC04LjkzLTguOTNIMTAuNDJ6IiBmaWxsPSIjZmZmIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNS45OTggMjYuMDQ5Yy01LjU0OSAwLTEwLjA0Ny00LjQ5OC0xMC4wNDctMTAuMDQ3IDAtNS41NDggNC40OTgtMTAuMDQ2IDEwLjA0Ny0xMC4wNDYgNS41NDggMCAxMC4wNDYgNC40OTggMTAuMDQ2IDEwLjA0NiAwIDUuNTQ5LTQuNDk4IDEwLjA0Ny0xMC4wNDYgMTAuMDQ3eiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMi43NjIgMTQuMjU0YzAtLjgyMi42NjctMS40ODkgMS40ODktMS40ODloMy40OTdjLjgyMiAwIDEuNDg4LjY2NiAxLjQ4OCAxLjQ4OXYzLjQ5N2MwIC44MjItLjY2NiAxLjQ4OC0xLjQ4OCAxLjQ4OGgtMy40OTdhMS40ODggMS40ODggMCAwMS0xLjQ4OS0xLjQ4OHYtMy40OTh6IiBmaWxsPSIjMTY1MkYwIi8+PC9zdmc+`; + } +} + export interface SnackbarOptions { darkMode: boolean; } @@ -18,6 +27,7 @@ export interface SnackbarInstanceProps { message?: string; menuItems?: SnackbarMenuItem[]; autoExpand?: boolean; + appSrc?: string | null; } export interface SnackbarMenuItem { @@ -97,6 +107,7 @@ export const SnackbarInstance: FunctionComponent = ({ autoExpand, message, menuItems, + appSrc, }) => { const [hidden, setHidden] = useState(true); const [expanded, setExpanded] = useState(autoExpand ?? false); @@ -129,7 +140,10 @@ export const SnackbarInstance: FunctionComponent = ({ )} >
- +
{message}
{!expanded && ( diff --git a/packages/wallet-sdk/src/components/SnackbarContainer.test.tsx b/packages/wallet-sdk/src/components/Snackbar/SnackbarContainer.test.tsx similarity index 100% rename from packages/wallet-sdk/src/components/SnackbarContainer.test.tsx rename to packages/wallet-sdk/src/components/Snackbar/SnackbarContainer.test.tsx diff --git a/packages/wallet-sdk/src/components/Spinner.scss b/packages/wallet-sdk/src/components/Spinner/Spinner.scss similarity index 99% rename from packages/wallet-sdk/src/components/Spinner.scss rename to packages/wallet-sdk/src/components/Spinner/Spinner.scss index 51eaf18ade..2481397e91 100644 --- a/packages/wallet-sdk/src/components/Spinner.scss +++ b/packages/wallet-sdk/src/components/Spinner/Spinner.scss @@ -8,7 +8,6 @@ svg { display: inline-block; animation: 2s linear infinite -cbwsdk-spinner-svg; - circle { animation: 1.9s ease-in-out infinite both -cbwsdk-spinner-circle; display: block; diff --git a/packages/wallet-sdk/src/components/Spinner.test.tsx b/packages/wallet-sdk/src/components/Spinner/Spinner.test.tsx similarity index 100% rename from packages/wallet-sdk/src/components/Spinner.test.tsx rename to packages/wallet-sdk/src/components/Spinner/Spinner.test.tsx diff --git a/packages/wallet-sdk/src/components/Spinner.tsx b/packages/wallet-sdk/src/components/Spinner/Spinner.tsx similarity index 100% rename from packages/wallet-sdk/src/components/Spinner.tsx rename to packages/wallet-sdk/src/components/Spinner/Spinner.tsx diff --git a/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.scss b/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.scss new file mode 100644 index 0000000000..85273c012f --- /dev/null +++ b/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.scss @@ -0,0 +1,140 @@ +// Copyright (c) 2018-2022 Coinbase, Inc. +// Licensed under the Apache License, version 2.0 + +@import "../theme.scss"; + +.-cbwsdk-css-reset { + .-cbwsdk-try-extension { + display: flex; + margin-top: 12px; + height: 202px; + width: 700px; + border-radius: 12px; + padding: 30px; + + &.light { + background: $white; + } + + &.dark { + background: $black; + } + + &-column-half { + flex: 50%; + } + + &-heading { + font-style: normal; + font-weight: 500; + font-size: 25px; + line-height: 32px; + margin: 0; + max-width: 204px; + + &.light { + color: $black; + } + + &.dark { + color: $white; + } + } + + &-cta { + appearance: none; + border: none; + background: none; + color: $primary; + cursor: pointer; + padding: 0; + text-decoration: none; + display: block; + font-weight: 600; + font-size: 16px; + line-height: 24px; + + &.light { + color: $primary; + } + + &.dark { + color: $primary-dark; + } + } + + &-cta-wrapper { + display: flex; + align-items: center; + margin-top: 12px; + } + + &-cta-icon { + display: block; + margin-left: 4px; + height: 14px; + } + + &-list { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 0; + padding: 0; + list-style: none; + height: 100%; + } + + &-list-item { + display: flex; + align-items: center; + flex-flow: nowrap; + margin-top: 24px; + + &:first-of-type { + margin-top: 0; + } + } + + &-list-item-icon-wrapper { + display: block; + } + + &-list-item-icon { + display: flex; + height: 32px; + width: 32px; + border-radius: 50%; + + svg { + margin: auto; + display: block; + } + + &.light { + background: $background-alt; + } + + &.dark { + background: $background-alt-dark; + } + } + + &-list-item-copy { + display: block; + font-weight: 400; + font-size: 14px; + line-height: 20px; + padding-left: 12px; + + &.light { + color: $foreground-muted; + } + + &.dark { + color: $foreground-muted-dark; + } + } + } +} diff --git a/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.tsx b/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.tsx new file mode 100644 index 0000000000..0f57b5f810 --- /dev/null +++ b/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.tsx @@ -0,0 +1,88 @@ +// Copyright (c) 2018-2022 Coinbase, Inc. +// Licensed under the Apache License, version 2.0 + +import clsx from "clsx"; +import { h } from "preact"; +import { useCallback, useState } from "preact/hooks"; + +import { ArrowLeftIcon } from "../icons/ArrowLeftIcon"; +import { LaptopIcon } from "../icons/LaptopIcon"; +import { SafeIcon } from "../icons/SafeIcon"; +import { Theme } from "../types"; +import css from "./TryExtensionContent-css"; + +type TryExtensionContentProps = { + theme: Theme; +}; + +export function TryExtensionContent({ theme }: TryExtensionContentProps) { + const [clicked, setClicked] = useState(false); + + const handleInstallClick = useCallback(() => { + window.open( + "https://api.wallet.coinbase.com/rpc/v2/desktop/chrome", + "_blank", + ); + }, []); + + const handleClick = useCallback(() => { + if (clicked) { + window.location.reload(); + } else { + handleInstallClick(); + setClicked(true); + } + }, [handleInstallClick, clicked]); + + return ( +
+ +
+

+ Or try the Coinbase Wallet browser extension +

+
+ +
+ {!clicked && ( + + )} +
+
+
+
+
    +
  • +
    + + + +
    +
    + Connect with dapps with just one click on your desktop browser +
    +
  • +
  • +
    + + + +
    +
    + Add an additional layer of security by using a supported Ledger + hardware wallet +
    +
  • +
+
+
+ ); +} diff --git a/packages/wallet-sdk/src/components/TryExtensionLinkDialog.scss b/packages/wallet-sdk/src/components/TryExtensionLinkDialog.scss deleted file mode 100644 index d5ebab082c..0000000000 --- a/packages/wallet-sdk/src/components/TryExtensionLinkDialog.scss +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright (c) 2018-2022 Coinbase, Inc. -// Licensed under the Apache License, version 2.0 - -.-cbwsdk-css-reset { - .-cbwsdk-extension-dialog { - z-index: 2147483647; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - - $blue-normal: #1652f0; - $blue-dark: rgb(55, 115, 245); - $wash: #fafbfc; - $white: #fff; - $black: #000; - $grey-50: #aaa; - $background-alt-dark: rgb(20, 21, 25); - - $elevation-z3: 0px 4px 12px rgba($black, 0.1); - $elevation-z4: 0px 0px 8px rgba($black, 0.04), - 0px 16px 24px rgba($black, 0.06); - - &-backdrop { - z-index: 2147483647; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - transition: opacity 0.25s; - - &.light { - background-color: rgba($black, 0.5); - } - - &.dark { - background-color: rgba(50, 53, 61, 0.4); - } - - &-hidden { - opacity: 0; - } - } - - &-box { - display: flex; - position: relative; - max-width: 500px; - flex-direction: column; - transform: scale(1); - transition: opacity 0.25s, transform 0.25s; - - &-hidden { - opacity: 0; - transform: scale(0.85); - } - - &-top { - display: flex; - flex-direction: row; - border-radius: 8px; - overflow: hidden; - min-height: 300px; - - &.dark { - color: $white; - background-color: $black; - box-shadow: 0 4px 16px rgba(255, 255, 255, 0.05); - } - - &.light { - background-color: $white; - } - - &-subtext { - margin-top: 15px; - font-size: 12px; - line-height: 1.5; - } - - &-install-region { - display: flex; - flex-basis: 50%; - flex-direction: column; - justify-content: center; - padding: 32px; - - button { - display: block; - border-radius: 8px; - background-color: $blue-normal; - color: $white; - width: 90%; - min-width: fit-content; - height: 44px; - margin-top: 16px; - font-size: 16px; - padding-left: 16px; - padding-right: 16px; - cursor: pointer; - font-weight: 500; - text-align: center; - - &.dark { - background-color: $blue-dark; - } - } - } - - &-info-region { - display: flex; - flex-basis: 50%; - flex-direction: column; - justify-content: center; - - &.light { - background-color: $wash; - } - - &.dark { - background-color: $background-alt-dark; - } - } - - &-description { - display: flex; - flex-direction: row; - align-items: center; - padding-top: 14px; - padding-bottom: 14px; - padding-left: 24px; - padding-right: 32px; - - &-icon-wrapper { - display: block; - position: relative; - width: 40px; - height: 40px; - flex-shrink: 0; - flex-grow: 0; - border-radius: 20px; - background-color: $white; - box-shadow: $elevation-z4; - - img { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; - } - } - - &-text { - margin-left: 16px; - flex-grow: 1; - font-size: 13px; - line-height: 19px; - align-self: center; - - &.light { - color: $black; - } - - &.dark { - color: white; - } - } - } - } - - &-bottom { - display: flex; - flex-direction: row; - overflow: hidden; - border-radius: 8px; - margin-top: 8px; - - &.light { - background-color: $white; - } - - &.dark { - background-color: $black; - box-shadow: 0 4px 16px rgba(255, 255, 255, 0.05); - } - - &-description-region { - display: flex; - flex-direction: column; - justify-content: center; - padding: 32px; - flex-grow: 1; - } - - &-description { - font-size: 13px; - line-height: 19px; - margin-top: 12px; - color: $grey-50; - - &.dark { - color: $white; - - a { - color: $blue-dark; - } - } - - a { - font-size: inherit; - line-height: inherit; - color: $blue-normal; - cursor: pointer; - } - } - - &-qr-region { - position: relative; - flex-shrink: 0; - display: flex; - flex-direction: column; - justify-content: center; - padding-left: 24px; - padding-right: 24px; - padding-top: 16px; - padding-bottom: 16px; - } - - &-qr-wrapper { - position: relative; - display: block; - padding: 8px; - border-radius: 8px; - box-shadow: $elevation-z3; - background-color: $white; - - img { - display: block; - } - } - - &-qr-connecting { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - &.light { - background-color: rgba($white, 0.95); - > p { - color: $black; - } - } - - &.dark { - background-color: rgba($background-alt-dark, 0.9); - > p { - color: $white; - } - } - - > p { - font-size: 12px; - font-weight: bold; - - margin-top: 16px; - } - } - } - - &-cancel { - position: absolute; - -webkit-appearance: none; - display: flex; - align-items: center; - justify-content: center; - top: 16px; - right: 16px; - width: 24px; - height: 24px; - border-radius: 12px; - - cursor: pointer; - - &.light { - background-color: $wash; - } - - &.dark { - background-color: $background-alt-dark; - } - - &-x { - position: relative; - display: block; - cursor: pointer; - - &.light { - &::before, - &::after { - background-color: $black; - } - } - - &.dark { - &::before, - &::after { - background-color: $white; - } - } - - &::before, - &::after { - content: ""; - position: absolute; - display: block; - top: -1px; - left: -7px; - width: 14px; - height: 1px; - transition: background-color 0.2s; - } - - &::before { - transform: rotate(45deg); - } - - &::after { - transform: rotate(135deg); - } - } - - &:hover { - .-cbwsdk-link-dialog-box-cancel-x { - &-a, - &-b { - background-color: #000; - } - } - } - } - } - - &-container { - display: block; - - &-hidden { - display: none; - } - } - - h2 { - display: block; - text-align: left; - font-size: 22px; - font-weight: 600; - line-height: 28px; - - &.light { - color: $black; - } - - &.dark { - color: $white; - } - } - } -} diff --git a/packages/wallet-sdk/src/components/TryExtensionLinkDialog.tsx b/packages/wallet-sdk/src/components/TryExtensionLinkDialog.tsx deleted file mode 100644 index df00c56ef0..0000000000 --- a/packages/wallet-sdk/src/components/TryExtensionLinkDialog.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import clsx from "clsx"; -import { FunctionComponent, h } from "preact"; -import { useCallback, useEffect, useState } from "preact/hooks"; - -import { createQrUrl } from "../util"; -import { LIB_VERSION } from "../version"; -import globeIcon from "./icons/globe-icon-svg"; -import linkIcon from "./icons/link-icon-svg"; -import lockIcon from "./icons/lock-icon-svg"; -import walletLogo from "./icons/QRLogo"; -import { QRCode } from "./QRCode"; -import { Spinner } from "./Spinner"; -import css from "./TryExtensionLinkDialog-css"; - -export const TryExtensionLinkDialog: FunctionComponent<{ - darkMode: boolean; - version: string; - sessionId: string; - sessionSecret: string; - linkAPIUrl: string; - isOpen: boolean; - isConnected: boolean; - isParentConnection: boolean; - chainId: number; - connectDisabled: boolean; - onCancel: (() => void) | null; -}> = props => { - const { isOpen, darkMode } = props; - const [isContainerHidden, setContainerHidden] = useState(!isOpen); - const [isDialogHidden, setDialogHidden] = useState(!isOpen); - - useEffect(() => { - const timers = [ - window.setTimeout(() => { - setDialogHidden(!isOpen); - }, 10), - ]; - - if (isOpen) { - setContainerHidden(false); - } else { - timers.push( - window.setTimeout(() => { - setContainerHidden(true); - }, 360), - ); - } - - return () => { - timers.forEach(window.clearTimeout); - }; - }, [props.isOpen]); - - const theme = darkMode ? "dark" : "light"; - - return ( -
- -
-
-
- { - window.open( - "https://api.wallet.coinbase.com/rpc/v2/desktop/chrome", - "_blank", - ); - }} - /> - {!props.connectDisabled ? ( - - ) : null} - - {props.onCancel && ( - - )} -
-
-
- ); -}; - -const TryExtensionBox: FunctionComponent<{ - darkMode: boolean; - onInstallClick: () => void; -}> = ({ darkMode, onInstallClick }) => { - const [isClicked, setIsClicked] = useState(false); - - const clickHandler = useCallback(() => { - if (isClicked) { - window.location.reload(); - } else { - onInstallClick(); - setIsClicked(true); - } - }, [onInstallClick, isClicked]); - - const theme = darkMode ? "dark" : "light"; - - return ( -
-
-

Try the Coinbase Wallet extension

- {isClicked && ( -
- After installing Coinbase Wallet, refresh the page and connect - again. -
- )} - -
-
- - - -
-
- ); -}; - -const ScanQRBox: FunctionComponent<{ - darkMode: boolean; - version: string; - sessionId: string; - sessionSecret: string; - linkAPIUrl: string; - isConnected: boolean; - isParentConnection: boolean; - chainId: number; -}> = props => { - const qrUrl = createQrUrl( - props.sessionId, - props.sessionSecret, - props.linkAPIUrl, - props.isParentConnection, - props.version, - props.chainId, - ); - - const theme = props.darkMode ? "dark" : "light"; - - return ( -
-
-

Or scan to connect

- - Open{" "} - - Coinbase Wallet - {" "} - on your mobile phone and scan - -
-
-
- -
- - - {!props.isConnected && ( -
- -

Connecting...

-
- )} -
-
- ); -}; - -const DescriptionItem: FunctionComponent<{ - icon: string; - text: string; - darkMode: boolean; -}> = props => { - const theme = props.darkMode ? "dark" : "light"; - return ( -
-
- -
- - {props.text} - -
- ); -}; - -const CancelButton: FunctionComponent<{ - onClick: () => void; - darkMode: boolean; -}> = props => { - const theme = props.darkMode ? "dark" : "light"; - return ( - - ); -}; diff --git a/packages/wallet-sdk/src/components/icons/ArrowLeftIcon.tsx b/packages/wallet-sdk/src/components/icons/ArrowLeftIcon.tsx new file mode 100644 index 0000000000..fccbbccdf8 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/ArrowLeftIcon.tsx @@ -0,0 +1,15 @@ +import { h } from "preact"; + +export function ArrowLeftIcon(props: h.JSX.SVGAttributes) { + return ( + + + + ); +} diff --git a/packages/wallet-sdk/src/components/icons/CloseIcon.tsx b/packages/wallet-sdk/src/components/icons/CloseIcon.tsx new file mode 100644 index 0000000000..ffaa6144d9 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/CloseIcon.tsx @@ -0,0 +1,16 @@ +import { h } from "preact"; + +export function CloseIcon(props: h.JSX.SVGAttributes) { + return ( + + + + ); +} diff --git a/packages/wallet-sdk/src/components/icons/LaptopIcon.tsx b/packages/wallet-sdk/src/components/icons/LaptopIcon.tsx new file mode 100644 index 0000000000..5ab844f845 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/LaptopIcon.tsx @@ -0,0 +1,16 @@ +import { h } from "preact"; + +export function LaptopIcon(props: h.JSX.SVGAttributes) { + return ( + + + + + ); +} diff --git a/packages/wallet-sdk/src/components/icons/QRCodeIcon.tsx b/packages/wallet-sdk/src/components/icons/QRCodeIcon.tsx new file mode 100644 index 0000000000..5bc6cbb96f --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/QRCodeIcon.tsx @@ -0,0 +1,23 @@ +import { h } from "preact"; + +export function QRCodeIcon(props: h.JSX.SVGAttributes) { + return ( + + + + + + + + + + + + ); +} diff --git a/packages/wallet-sdk/src/components/icons/QRLogo.ts b/packages/wallet-sdk/src/components/icons/QRLogo.ts deleted file mode 100644 index 9384ad299c..0000000000 --- a/packages/wallet-sdk/src/components/icons/QRLogo.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default ` - - - - - - -`; diff --git a/packages/wallet-sdk/src/components/icons/QRLogoCoinbase.tsx b/packages/wallet-sdk/src/components/icons/QRLogoCoinbase.tsx new file mode 100644 index 0000000000..2cb556ee8d --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/QRLogoCoinbase.tsx @@ -0,0 +1,9 @@ +const svg = ` + + + + + +`; + +export default svg; diff --git a/packages/wallet-sdk/src/components/icons/QRLogoWallet.ts b/packages/wallet-sdk/src/components/icons/QRLogoWallet.ts new file mode 100644 index 0000000000..2bee8c9bf6 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/QRLogoWallet.ts @@ -0,0 +1,8 @@ +export default ` + + + + + + +`; diff --git a/packages/wallet-sdk/src/components/icons/SafeIcon.tsx b/packages/wallet-sdk/src/components/icons/SafeIcon.tsx new file mode 100644 index 0000000000..a5c106279c --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/SafeIcon.tsx @@ -0,0 +1,19 @@ +import { h } from "preact"; + +export function SafeIcon(props: h.JSX.SVGAttributes) { + return ( + + + + ); +} diff --git a/packages/wallet-sdk/src/components/icons/StatusDotIcon.tsx b/packages/wallet-sdk/src/components/icons/StatusDotIcon.tsx new file mode 100644 index 0000000000..254205f535 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/StatusDotIcon.tsx @@ -0,0 +1,19 @@ +import { h } from "preact"; + +export function StatusDotIcon(props: h.JSX.SVGAttributes) { + return ( + + + + ); +} diff --git a/packages/wallet-sdk/src/components/icons/coinbase-round.svg b/packages/wallet-sdk/src/components/icons/coinbase-round.svg new file mode 100644 index 0000000000..e877142519 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/coinbase-round.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/wallet-sdk/src/components/icons/coinbase-wallet-round.svg b/packages/wallet-sdk/src/components/icons/coinbase-wallet-round.svg new file mode 100644 index 0000000000..18c5c743d7 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/coinbase-wallet-round.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/wallet-sdk/src/components/icons/coinbase.svg b/packages/wallet-sdk/src/components/icons/coinbase.svg new file mode 100644 index 0000000000..562f1d43a4 --- /dev/null +++ b/packages/wallet-sdk/src/components/icons/coinbase.svg @@ -0,0 +1,20 @@ + + + + + diff --git a/packages/wallet-sdk/src/components/icons/globe-icon.svg b/packages/wallet-sdk/src/components/icons/globe-icon.svg deleted file mode 100644 index b20b7c800a..0000000000 --- a/packages/wallet-sdk/src/components/icons/globe-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/wallet-sdk/src/components/icons/link-icon.svg b/packages/wallet-sdk/src/components/icons/link-icon.svg deleted file mode 100644 index 6c4fa19320..0000000000 --- a/packages/wallet-sdk/src/components/icons/link-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/wallet-sdk/src/components/icons/lock-icon.svg b/packages/wallet-sdk/src/components/icons/lock-icon.svg deleted file mode 100644 index 083c19f946..0000000000 --- a/packages/wallet-sdk/src/components/icons/lock-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/wallet-sdk/src/components/theme.scss b/packages/wallet-sdk/src/components/theme.scss new file mode 100644 index 0000000000..7032491c4d --- /dev/null +++ b/packages/wallet-sdk/src/components/theme.scss @@ -0,0 +1,16 @@ +$primary: #0052ff; +$primary-dark: #588af5; + +$primary-wash: #f5f8ff; +$primary-wash-dark: #001033; + +$foreground-muted: #5b636e; +$foreground-muted-dark: #8a919e; + +$white: #fff; +$black: #0a0b0d; + +$background: $white; +$background-alt: #eef0f3; +$background-dark: $black; +$background-alt-dark: #1e2025; diff --git a/packages/wallet-sdk/src/components/types.ts b/packages/wallet-sdk/src/components/types.ts new file mode 100644 index 0000000000..695da5b851 --- /dev/null +++ b/packages/wallet-sdk/src/components/types.ts @@ -0,0 +1 @@ +export type Theme = "dark" | "light"; diff --git a/packages/wallet-sdk/src/lib/cssReset.scss b/packages/wallet-sdk/src/lib/cssReset.scss index 8d36746a66..5f2fa7e065 100644 --- a/packages/wallet-sdk/src/lib/cssReset.scss +++ b/packages/wallet-sdk/src/lib/cssReset.scss @@ -55,7 +55,6 @@ border-top-right-radius: 0; border-top-style: none; border-top-width: medium; - bottom: auto; box-shadow: none; box-sizing: border-box; caption-side: top; @@ -72,12 +71,9 @@ column-rule-width: none; column-span: 1; column-width: auto; - content: normal; counter-increment: none; counter-reset: none; - cursor: auto; direction: ltr; - display: block; empty-cells: show; float: none; font: normal; @@ -89,7 +85,6 @@ font-weight: normal; height: auto; hyphens: none; - left: auto; letter-spacing: normal; line-height: normal; list-style: none; @@ -101,10 +96,6 @@ margin-left: 0; margin-right: 0; margin-top: 0; - max-height: none; - max-width: none; - min-height: 0; - min-width: 0; opacity: 1; orphans: 0; outline: 0; @@ -127,7 +118,6 @@ pointer-events: auto; position: static; quotes: "\\201C""\\201D""\\2018""\\2019"; - right: auto; tab-size: 8; table-layout: auto; text-align: inherit; @@ -139,7 +129,6 @@ text-indent: 0; text-shadow: none; text-transform: none; - top: auto; transform: none; transform-style: flat; transition: none; @@ -152,14 +141,16 @@ visibility: visible; white-space: normal; widows: 0; - width: auto; word-spacing: normal; z-index: auto; } + strong { + font-weight: bold; + } + * { box-sizing: border-box; - display: initial; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif; line-height: 1; diff --git a/packages/wallet-sdk/src/provider/WalletSDKUI.test.ts b/packages/wallet-sdk/src/provider/WalletSDKUI.test.ts index 13f8a0bf77..3da388f67a 100644 --- a/packages/wallet-sdk/src/provider/WalletSDKUI.test.ts +++ b/packages/wallet-sdk/src/provider/WalletSDKUI.test.ts @@ -2,8 +2,8 @@ import { render } from "@testing-library/preact"; import { Session } from "inspector"; import { Observable, Subject } from "rxjs"; -import { LinkFlow } from "../components/LinkFlow"; -import { Snackbar } from "../components/Snackbar"; +import { LinkFlow } from "../components/LinkFlow/LinkFlow"; +import { Snackbar } from "../components/Snackbar/Snackbar"; import { WalletSDKUI } from "./WalletSDKUI"; describe("WalletSDKUI", () => { @@ -44,6 +44,7 @@ describe("WalletSDKUI", () => { expect(snackbarMock).toHaveBeenCalledWith({ message: "Confirm on phone", + appSrc: null, menuItems: [ { isRed: true, @@ -83,6 +84,7 @@ describe("WalletSDKUI", () => { expect(snackbarMock).toHaveBeenCalledWith({ autoExpand: true, message: "Connection lost", + appSrc: null, menuItems: [ { isRed: false, diff --git a/packages/wallet-sdk/src/provider/WalletSDKUI.ts b/packages/wallet-sdk/src/provider/WalletSDKUI.ts index 5d542387ba..3b64fca21f 100644 --- a/packages/wallet-sdk/src/provider/WalletSDKUI.ts +++ b/packages/wallet-sdk/src/provider/WalletSDKUI.ts @@ -1,5 +1,8 @@ -import { LinkFlow } from "../components/LinkFlow"; -import { Snackbar, SnackbarInstanceProps } from "../components/Snackbar"; +import { LinkFlow } from "../components/LinkFlow/LinkFlow"; +import { + Snackbar, + SnackbarInstanceProps, +} from "../components/Snackbar/Snackbar"; import { injectCssReset } from "../lib/cssReset"; import { EthereumAddressFromSignedMessageRequest, @@ -20,6 +23,7 @@ export class WalletSDKUI implements WalletUI { private readonly snackbar: Snackbar; private standalone: boolean | null = null; private attached = false; + private appSrc: string | null = null; constructor(options: Readonly) { this.snackbar = new Snackbar({ @@ -154,6 +158,7 @@ export class WalletSDKUI implements WalletUI { snackbarProps = { autoExpand: true, message: "Connection lost", + appSrc: this.appSrc, menuItems: [ { isRed: false, @@ -170,6 +175,7 @@ export class WalletSDKUI implements WalletUI { } else { snackbarProps = { message: "Confirm on phone", + appSrc: this.appSrc, menuItems: [ { isRed: true, @@ -198,6 +204,11 @@ export class WalletSDKUI implements WalletUI { return this.snackbar.presentItem(snackbarProps); } + /* istanbul ignore next */ + setAppSrc(appSrc: string): void { + this.appSrc = appSrc; + } + /* istanbul ignore next */ reloadUI(): void { document.location.reload(); diff --git a/packages/wallet-sdk/src/provider/WalletUI.ts b/packages/wallet-sdk/src/provider/WalletUI.ts index 5c715e9199..fd2bb37a15 100644 --- a/packages/wallet-sdk/src/provider/WalletUI.ts +++ b/packages/wallet-sdk/src/provider/WalletUI.ts @@ -157,6 +157,11 @@ export interface WalletUI { */ isStandalone(): boolean; + /** + * Set the UI's app src for different apps + */ + setAppSrc(src: string): void; + /** * We want to disable showing the qr code for in-page connection if the dapp hasn't provided a json rpc url */ diff --git a/packages/wallet-sdk/src/relay/WalletSDKRelay.ts b/packages/wallet-sdk/src/relay/WalletSDKRelay.ts index 2a1d10197d..3789c123b1 100644 --- a/packages/wallet-sdk/src/relay/WalletSDKRelay.ts +++ b/packages/wallet-sdk/src/relay/WalletSDKRelay.ts @@ -362,6 +362,25 @@ export class WalletSDKRelay extends WalletSDKRelayAbstract { }), ); + this.subscriptions.add( + connection.sessionConfig$ + .pipe(filter(c => c.metadata && c.metadata.AppSrc !== undefined)) + .pipe( + mergeMap(c => aes256gcm.decrypt(c.metadata.AppSrc!, session.secret)), + ) + .subscribe({ + next: appSrc => { + this.ui.setAppSrc(appSrc); + }, + error: () => { + this.diagnostic?.log(EVENTS.GENERAL_ERROR, { + message: "Had error decrypting", + value: "appSrc", + }); + }, + }), + ); + const ui = this.options.uiConstructor({ linkAPIUrl: this.options.linkAPIUrl, version: this.options.version, @@ -879,7 +898,7 @@ export class WalletSDKRelay extends WalletSDKRelayAbstract { location = window.location; } - location.href = `https://go.cb-w.com/xoXnYwQimhb?cb_url=${encodeURIComponent( + location.href = `https://www.coinbase.com/connect-dapp?uri=${encodeURIComponent( location.href, )}`; return;