Skip to content

Commit

Permalink
Handle in page provider (#54)
Browse files Browse the repository at this point in the history
* WIP - Show correct dialog UI based on feature flag

* Use in page provider if available

* Override setAppInfo.  Implement setting chain id and custom rpc url.

* Fix formatting

* Add ability to set provider info to wallet link provider

* Add setAppInfo method to provider

* Fix lint error

* Fix typo

* Remove non-working feature flag code

* Revert "Remove non-working feature flag code"

This reverts commit 7c2328412b209fc1055c11704683ceb836932c6e.

* Remove todo comments

Co-authored-by: Alex Shoykhet <alex.shoykhet@coinbase.com>
  • Loading branch information
2 people authored and GitHub Enterprise committed Apr 20, 2021
1 parent e05630b commit 7a406e1
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 31 deletions.
42 changes: 33 additions & 9 deletions js/src/WalletLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface WalletLinkOptions {
walletLinkUIConstructor?: (
options: Readonly<WalletLinkUIOptions>
) => WalletLinkUI
/** optional whether wallet link provider should override the isMetaMask property. */
/** @optional whether wallet link provider should override the isMetaMask property. */
overrideIsMetaMask?: boolean
}

Expand All @@ -44,8 +44,8 @@ export class WalletLink {

private _appName = ""
private _appLogoUrl: string | null = null
private _relay: WalletLinkRelay
private _relayEventManager: WalletLinkRelayEventManager
private _relay: WalletLinkRelay | null = null
private _relayEventManager: WalletLinkRelayEventManager | null = null
private _storage: ScopedLocalStorage
private _overrideIsMetaMask: boolean

Expand All @@ -72,9 +72,11 @@ export class WalletLink {

const u = url.parse(walletLinkUrl)
const walletLinkOrigin = `${u.protocol}//${u.host}`
this._storage = new ScopedLocalStorage(
`-walletlink:${walletLinkOrigin}`
)
this._storage = new ScopedLocalStorage(`-walletlink:${walletLinkOrigin}`)

if (typeof window.walletLinkExtension !== "undefined") {
return
}

this._relayEventManager = new WalletLinkRelayEventManager()

Expand All @@ -100,8 +102,20 @@ export class WalletLink {
jsonRpcUrl: string,
chainId: number = 1
): WalletLinkProvider {
if (typeof window.walletLinkExtension !== "undefined") {
//@ts-ignore
window.walletLinkExtension.setProviderInfo(jsonRpcUrl, chainId)

return window.walletLinkExtension
}

const relay = this._relay
if (!relay || !this._relayEventManager || !this._storage) {
throw new Error("Relay not initialized, should never happen")
}

return new WalletLinkProvider({
relayProvider: () => Promise.resolve(this._relay),
relayProvider: () => Promise.resolve(relay),
relayEventManager: this._relayEventManager,
storage: this._storage,
jsonRpcUrl,
Expand All @@ -121,14 +135,24 @@ export class WalletLink {
): void {
this._appName = appName || "DApp"
this._appLogoUrl = appLogoUrl || getFavicon()
this._relay.setAppInfo(this._appName, this._appLogoUrl)

if (typeof window.walletLinkExtension !== "undefined") {
//@ts-ignore
window.walletLinkExtension.setAppInfo(this._appName, this._appLogoUrl)
} else {
this._relay?.setAppInfo(this._appName, this._appLogoUrl)
}
}

/**
* Disconnect. After disconnecting, this will reload the web page to ensure
* all potential stale state is cleared.
*/
public disconnect(): void {
this._relay.resetAndReload()
if (typeof window.walletLinkExtension !== "undefined") {
window.walletLinkExtension.close()
} else {
this._relay?.resetAndReload()
}
}
}
81 changes: 64 additions & 17 deletions js/src/components/LinkFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// Licensed under the Apache License, version 2.0

import { h, render } from "preact"
import { Observable, Subscription } from "rxjs"
import { BehaviorSubject, Observable, Subscription } from "rxjs"
import { LinkDialog } from "./LinkDialog"
import { first } from "rxjs/operators"
import { TryExtensionLinkDialog } from "./TryExtensionLinkDialog"

export interface LinkFlowOptions {
darkMode: boolean
Expand All @@ -16,6 +18,10 @@ export interface LinkFlowOptions {
connected$: Observable<boolean>
}

interface Optional<T> {
value?: T
}

export class LinkFlow {
private readonly darkMode: boolean
private readonly version: string
Expand All @@ -25,6 +31,9 @@ export class LinkFlow {
private readonly isParentConnection: boolean

private readonly connected$: Observable<boolean>
private readonly extensionUI$: BehaviorSubject<
Optional<boolean>
> = new BehaviorSubject({})
private readonly subscriptions = new Subscription()

private isConnected = false
Expand All @@ -41,6 +50,22 @@ export class LinkFlow {
this.walletLinkUrl = options.walletLinkUrl
this.isParentConnection = options.isParentConnection
this.connected$ = options.connected$

// Check if extension UI is enabled
fetch("https://api.wallet.coinbase.com/rpc/v2/getFeatureFlags")
.then(res => res.json())
.then(json => {
const enabled: boolean | undefined = json.result.desktop.extension_ui
if (typeof enabled === "undefined") {
this.extensionUI$.next({ value: false })
} else {
this.extensionUI$.next({ value: enabled })
}
})
.catch(err => {
console.error(`Couldn't fetch feature flags - ${err}`)
this.extensionUI$.next({ value: false })
})
}

public attach(el: Element): void {
Expand Down Expand Up @@ -85,21 +110,43 @@ export class LinkFlow {
return
}

// TODO - Vishnu: Use feature flag to show correct dialog UI

render(
<LinkDialog
darkMode={this.darkMode}
version={this.version}
sessionId={this.sessionId}
sessionSecret={this.sessionSecret}
walletLinkUrl={this.walletLinkUrl}
isOpen={this.isOpen}
isConnected={this.isConnected}
isParentConnection={this.isParentConnection}
onCancel={this.onCancel}
/>,
this.root
)
const subscription = this.extensionUI$
.pipe(first(enabled => enabled.value !== undefined)) // wait for a valid value before rendering
.subscribe(enabled => {
if (!this.root) {
return
}

render(
enabled.value! ? (
<TryExtensionLinkDialog
darkMode={this.darkMode}
version={this.version}
sessionId={this.sessionId}
sessionSecret={this.sessionSecret}
walletLinkUrl={this.walletLinkUrl}
isOpen={this.isOpen}
isConnected={this.isConnected}
isParentConnection={this.isParentConnection}
onCancel={this.onCancel}
/>
) : (
<LinkDialog
darkMode={this.darkMode}
version={this.version}
sessionId={this.sessionId}
sessionSecret={this.sessionSecret}
walletLinkUrl={this.walletLinkUrl}
isOpen={this.isOpen}
isConnected={this.isConnected}
isParentConnection={this.isParentConnection}
onCancel={this.onCancel}
/>
),
this.root
)
})

this.subscriptions.add(subscription)
}
}
5 changes: 4 additions & 1 deletion js/src/components/TryExtensionLinkDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ export const TryExtensionLinkDialog: FunctionComponent<{
>
<TryExtensionBox
onInstallClick={() => {
// TODO - Vishnu: Redirect users to extension download page
window.open(
"https://api.wallet.coinbase.com/rpc/v2/desktop/chrome",
"_blank"
)
}}
/>
<ScanQRBox
Expand Down
1 change: 1 addition & 0 deletions js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ declare global {
WalletLink: typeof WalletLink
WalletLinkProvider: typeof WalletLinkProvider
ethereum?: WalletLinkProvider
walletLinkExtension?: WalletLinkProvider
}
}

Expand Down
14 changes: 12 additions & 2 deletions js/src/provider/WalletLinkProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export class WalletLinkProvider
private readonly _storage: ScopedLocalStorage
private readonly _relayEventManager: WalletLinkRelayEventManager

private readonly _chainId: IntNumber
private readonly _jsonRpcUrl: string
private _chainId: IntNumber
private _jsonRpcUrl: string
private readonly _overrideIsMetaMask: boolean

private _addresses: AddressString[] = []
Expand Down Expand Up @@ -135,6 +135,16 @@ export class WalletLinkProvider
return true
}

public setProviderInfo(jsonRpcUrl: string, chainId: number) {
this._jsonRpcUrl = jsonRpcUrl
this._chainId = ensureIntNumber(chainId)
this.emit("chainChanged", this._chainId)
}

public setAppInfo(appName: string, appLogoUrl: string | null): void {
this.initializeRelay().then(relay => relay.setAppInfo(appName, appLogoUrl))
}

public async enable(): Promise<AddressString[]> {
if (this._addresses.length > 0) {
return this._addresses
Expand Down
2 changes: 2 additions & 0 deletions js/src/relay/WalletLinkRelayAbstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,6 @@ export abstract class WalletLinkRelayAbstract {
abstract sendRequest<T extends Web3Request, U extends Web3Response>(
request: T
): Promise<U>

abstract setAppInfo(appName: string, appLogoUrl: string | null): void
}
1 change: 0 additions & 1 deletion js/src/relay/WalletLinkRelayEventManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export class WalletLinkRelayEventManager {
// unlikely that this will ever be an issue, but just to be safe
const callback = this.callbacks.get(idStr)
if (callback) {
// TODO - how to handle this case
this.callbacks.delete(idStr)
}
return id
Expand Down
2 changes: 1 addition & 1 deletion js/src/vendor-js/qrcode-svg/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ QRCode.prototype.svg = function(opt) {
modrect = indent + '<path x="0" y="0" style="fill:' + options.color + ';shape-rendering:crispEdges;" d="' + pathdata + '" />';
}
let imgSvg = "";
if(this.options.image.svg){
if(this.options.image !== undefined && this.options.image.svg){
const imgWidth = width * this.options.image.width / 100;
const imgHeight = height * this.options.image.height / 100;
const imgX = (width/2) - imgWidth/2;
Expand Down

0 comments on commit 7a406e1

Please sign in to comment.