diff --git a/README.md b/README.md index 4e8657b..046f846 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Calling `.connect()` (no params) with no previous successfull call, will throw. async connect( providerType?: ProviderType, chainId: ChainId = ChainId.MAINNET -): Promise +): Promise ``` **Usage** @@ -177,7 +177,7 @@ export enum ChainId { ### ConnectionResponse ```typescript -export type ConnectResponse = { +export type ConnectionResponse = { provider: Provider chainId: ChainId account: null | string @@ -189,13 +189,13 @@ export type ConnectResponse = { ```typescript import { connection, - ConnectResponse, + ConnectionResponse, ProviderType, ChainId } from 'decentraland-connection' async function connect() { - let result: ConnectResponse + let result: ConnectionResponse try { result = await connection.connect() // this will throw if no successful connect was called before } catch (error) { diff --git a/src/ConnectionManager.ts b/src/ConnectionManager.ts index 64dcc7f..e8a7038 100644 --- a/src/ConnectionManager.ts +++ b/src/ConnectionManager.ts @@ -10,7 +10,8 @@ import { RequestArguments, ProviderType, ChainId, - ConnectResponse, + ConnectionData, + ConnectionResponse, Provider, ClosableConnector, LegacyProvider @@ -19,7 +20,6 @@ import { getConfiguration } from './configuration' import './declarations' export class ConnectionManager { - providerType?: ProviderType connector?: AbstractConnector constructor(public storage: Storage) {} @@ -27,16 +27,18 @@ export class ConnectionManager { async connect( providerType?: ProviderType, chainId: ChainId = ChainId.MAINNET - ): Promise { - const { storageKey } = getConfiguration() - - this.providerType = providerType || this.storage.get(storageKey) - if (!this.providerType) { - throw new Error('connect called without a provider and none was stored') + ): Promise { + if (!providerType) { + const connectionData = this.getConnectionData() + if (!connectionData) { + throw new Error('connect called without a provider and none was stored') + } + providerType = connectionData.providerType + chainId = connectionData.chainId } - this.storage.set(storageKey, providerType) - this.connector = this.getConnector(this.providerType, chainId) + this.setConnectionData(providerType, chainId) + this.connector = this.getConnector(providerType, chainId) const { provider, @@ -65,6 +67,9 @@ export class ConnectionManager { if (this.isClosableConnector()) { await (this.connector as ClosableConnector).close() } + + this.storage.clear() + this.connector = undefined } } @@ -100,10 +105,23 @@ export class ConnectionManager { } } + private getConnectionData(): ConnectionData | undefined { + const { storageKey } = getConfiguration() + const connectionData = this.storage.get(storageKey) + return connectionData ? JSON.parse(connectionData) : undefined + } + + private setConnectionData(providerType: ProviderType, chainId: ChainId) { + const { storageKey } = getConfiguration() + const connectionData = JSON.stringify({ + providerType, + chainId + } as ConnectionData) + this.storage.set(storageKey, connectionData) + } + private isClosableConnector() { - return [ProviderType.FORTMATIC, ProviderType.WALLET_CONNECT].includes( - this.providerType! - ) + return this.connector && typeof this.connector['close'] !== 'undefined' } private toProvider(provider: LegacyProvider | Provider): Provider { diff --git a/src/storage/LocalStorage.ts b/src/storage/LocalStorage.ts index 170dfa8..afb0262 100644 --- a/src/storage/LocalStorage.ts +++ b/src/storage/LocalStorage.ts @@ -9,4 +9,8 @@ export class LocalStorage extends Storage { set(key: string, value: any): void { window.localStorage.setItem(key, value) } + + clear(): void { + window.localStorage.clear() + } } diff --git a/src/storage/Storage.ts b/src/storage/Storage.ts index 6c408a7..80ec31f 100644 --- a/src/storage/Storage.ts +++ b/src/storage/Storage.ts @@ -1,4 +1,5 @@ export abstract class Storage { abstract get(key: string): any | undefined abstract set(key: string, value: any): void + abstract clear(): void } diff --git a/src/types.ts b/src/types.ts index 40747d4..2b953de 100644 --- a/src/types.ts +++ b/src/types.ts @@ -28,7 +28,12 @@ export interface Provider extends EventEmitter { export type LegacyProvider = Pick -export type ConnectResponse = { +export type ConnectionData = { + providerType: ProviderType + chainId: ChainId +} + +export type ConnectionResponse = { provider: Provider chainId: ChainId account: null | string diff --git a/test/ConnectionManager.spec.ts b/test/ConnectionManager.spec.ts index da5673e..44f6646 100644 --- a/test/ConnectionManager.spec.ts +++ b/test/ConnectionManager.spec.ts @@ -1,6 +1,7 @@ import chai from 'chai' import chaiAsPromised from 'chai-as-promised' import sinon from 'sinon' +import { getConfiguration } from '../src/configuration' import { ConnectionManager, connection } from '../src/ConnectionManager' import { LocalStorage } from '../src/storage' import { ChainId, ClosableConnector, ProviderType } from '../src/types' @@ -20,7 +21,7 @@ describe('ConnectionManager', () => { afterEach(() => { sinon.restore() - storage.clean() + storage.clear() }) describe('connection', () => { @@ -30,15 +31,6 @@ describe('ConnectionManager', () => { }) describe('#connect', () => { - it('should set the provider type', async () => { - const stubConnector = new StubConnector() - sinon.stub(connectionManager, 'getConnector').returns(stubConnector) - - expect(connectionManager.providerType).to.eq(undefined) - await connectionManager.connect(ProviderType.INJECTED) - expect(connectionManager.providerType).to.eq(ProviderType.INJECTED) - }) - it('should set the connector', async () => { const stubConnector = new StubConnector() sinon.stub(connectionManager, 'getConnector').returns(stubConnector) @@ -112,11 +104,16 @@ describe('ConnectionManager', () => { it('should store the last provider and chain', async () => { const stubConnector = new StubConnector() + const configuration = getConfiguration() sinon.stub(connectionManager, 'getConnector').returns(stubConnector) - await connectionManager.connect(ProviderType.INJECTED) + await connectionManager.connect(ProviderType.INJECTED, ChainId.KOVAN) - expect(storage.get()).to.eq(ProviderType.INJECTED) + const value = JSON.stringify({ + providerType: ProviderType.INJECTED, + chainId: ChainId.KOVAN + }) + expect(storage.get(configuration.storageKey)).to.eq(value) }) it('should connect to the last supplied provider', async () => { @@ -166,23 +163,34 @@ describe('ConnectionManager', () => { }) it('should call close if the provider type allows it', async () => { - await deactivate(ProviderType.FORTMATIC, true) - await deactivate(ProviderType.WALLET_CONNECT, true) - await deactivate(ProviderType.INJECTED, false) - - async function deactivate(providerType: ProviderType, result: boolean) { - connectionManager.providerType = providerType - connectionManager.connector = new StubClosableConnector() - const closeStub = sinon.stub( - connectionManager.connector as ClosableConnector, - 'close' - ) + connectionManager.connector = new StubClosableConnector() + const closeStub = sinon.stub( + connectionManager.connector as ClosableConnector, + 'close' + ) - await connectionManager.disconnect() + await connectionManager.disconnect() - expect(closeStub.calledOnce).to.eq(result) - sinon.restore() - } + expect(closeStub.calledOnce).to.eq(true) + sinon.restore() + }) + + it('should clean the storage', async () => { + const configuration = getConfiguration() + storage.set(configuration.storageKey, 'data') + + connectionManager.connector = new StubConnector() + await connectionManager.disconnect() + + expect(storage.get(configuration.storageKey)).to.eq(undefined) + }) + + it('should clean the instance variables', async () => { + connectionManager.connector = new StubConnector() + + await connectionManager.disconnect() + + expect(connectionManager.connector).to.eq(undefined) }) }) diff --git a/test/utils.ts b/test/utils.ts index 8d90c3f..a0db250 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -39,17 +39,17 @@ export class StubClosableConnector extends StubConnector { } export class StubStorage extends Storage { - value: any + cache: Record = {} - get(_key: string = '') { - return this.value + get(key: string) { + return this.cache[key] } - set(_key: string, value: any) { - this.value = value + set(key: string, value: any) { + this.cache[key] = value } - clean() { - this.value = undefined + clear() { + this.cache = {} } }