import type { Observable } from "rxjs" import { switchMap } from "rxjs/operators" import type { Maybe } from "./common/utils" import type { ConnectionState } from "./connection-state" import { getStateConnected } from "./connection-state" /** * Provider of the connection. * Examples: injected web3, fortmatic, temple tezos wallet, blocto. */ export type ConnectionProvider = { /** * Returns unique identifier of the connection provider. It's used to save/load last connected provider */ getId(): string /** * Checks if this provider is auto-connected. For example, injected mobile providers are connected by default */ isAutoConnected(): Promise /** * List of available connection options: injected web3 can find out what option is available (Metamask, Trust etc.) */ getOption(): Promise> /** * Current connection state. If value is undefined, then provider is considered disconnected. */ getConnection(): Observable> /** * Checks if provider can establish connection without asking user permission (if session is not expired) */ isConnected(): Promise } export abstract class AbstractConnectionProvider implements ConnectionProvider { abstract getId(): string abstract getConnection(): Observable> abstract getOption(): Promise> abstract isAutoConnected(): Promise abstract isConnected(): Promise map( mapper: (c: C) => NewConnection | PromiseLike, ): ConnectionProvider { return new MappedConnectionProvider(this, mapper) } mapOption(mapper: (o: O) => NewOption): ConnectionProvider { return new MappedOptionConnectionProvider(this, mapper) } } export class MappedOptionConnectionProvider extends AbstractConnectionProvider { constructor( private readonly source: ConnectionProvider, private readonly mapper: (from: O) => NewO, ) { super() } getId(): string { return this.source.getId() } getConnection(): Observable> { return this.source.getConnection() } isAutoConnected() { return this.source.isAutoConnected() } async getOption() { const sourceOption = await this.source.getOption() return sourceOption ? this.mapper(sourceOption) : undefined } isConnected(): Promise { return this.source.isConnected() } } export class MappedConnectionProvider extends AbstractConnectionProvider { constructor( private readonly source: ConnectionProvider, private readonly mapper: (from: Connection) => NewConnection | PromiseLike ) { super() } getId(): string { return this.source.getId() } getConnection(): Observable> { return this.source.getConnection().pipe( switchMap(async state => { if (state.status === "connected") { const connection = await this.mapper(state.connection) return getStateConnected({ connection, disconnect: state.disconnect, }) } else { return state } })) } isAutoConnected(): Promise { return this.source.isAutoConnected() } getOption() { return this.source.getOption() } isConnected(): Promise { return this.source.isConnected() } }