Skip to content

Commit

Permalink
refactor: simplify wallet provider (#306)
Browse files Browse the repository at this point in the history
  • Loading branch information
tien authored Nov 3, 2024
1 parent 0958ce1 commit bbda9ef
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 110 deletions.
7 changes: 7 additions & 0 deletions .changeset/fuzzy-lions-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@reactive-dot/react": patch
"@reactive-dot/core": patch
"@reactive-dot/vue": patch
---

Simplified wallet provider interface.
35 changes: 10 additions & 25 deletions packages/core/src/actions/aggregate-wallets.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import type { MaybeAsync } from "../types.js";
import { toObservable } from "../utils/to-observable.js";
import type { WalletProvider } from "../wallets/index.js";
import { combineLatest, from, of } from "rxjs";
import { map, switchMap } from "rxjs/operators";
import { Wallet, type WalletProvider } from "../wallets/index.js";

export function aggregateWallets(providers: MaybeAsync<WalletProvider[]>) {
return toObservable(providers).pipe(
switchMap((providers) => {
if (providers.length === 0) {
return of([]);
}

return from(
Promise.all(
providers.map(async (provider) => {
await provider.scan();
return provider;
}),
),
);
}),
switchMap((providers) =>
combineLatest(providers.map((provider) => provider.wallets$)),
export function aggregateWallets(
providersOrWallets: ReadonlyArray<Wallet | WalletProvider>,
) {
return Promise.all(
providersOrWallets.map((walletOrProvider) =>
walletOrProvider instanceof Wallet
? [walletOrProvider]
: walletOrProvider.getWallets(),
),
map((wallets) => wallets.flat()),
);
).then((wallets) => wallets.flat());
}
15 changes: 1 addition & 14 deletions packages/core/src/wallets/initialize-wallets.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
import { WalletProvider } from "./provider.js";
import type { Wallet } from "./wallet.js";

export async function initializeWallets(
walletsOrProviders: Array<Wallet | WalletProvider>,
) {
const wallets = (
await Promise.all(
walletsOrProviders.map((walletOrProvider) =>
walletOrProvider instanceof WalletProvider
? walletOrProvider.scan()
: [walletOrProvider],
),
)
).flat();

export function initializeWallets(wallets: readonly Wallet[]) {
return Promise.all(wallets.map((wallet) => wallet.initialize()));
}
26 changes: 4 additions & 22 deletions packages/core/src/wallets/injected/provider.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,15 @@
import { WalletProvider } from "../provider.js";
import { InjectedWallet, type InjectedWalletOptions } from "./wallet.js";
import { getInjectedExtensions } from "polkadot-api/pjs-signer";
import { BehaviorSubject } from "rxjs";
import { map } from "rxjs/operators";

export class InjectedWalletProvider extends WalletProvider {
constructor(private readonly options?: InjectedWalletOptions) {
super();
}

readonly #walletMap$ = new BehaviorSubject(new Map<string, InjectedWallet>());

readonly wallets$ = this.#walletMap$.pipe(
map((walletMap) => Array.from(walletMap.values())),
);

scan() {
const injectedNames = getInjectedExtensions() ?? [];

const current = new Map(this.#walletMap$.value);

for (const name of injectedNames) {
if (!current.has(name)) {
current.set(name, new InjectedWallet(name, this.options));
}
}

this.#walletMap$.next(current);

return Array.from(current.values());
getWallets() {
return getInjectedExtensions().map(
(name) => new InjectedWallet(name, this.options),
);
}
}
5 changes: 1 addition & 4 deletions packages/core/src/wallets/provider.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { Wallet } from "./wallet.js";
import type { Observable } from "rxjs";

export abstract class WalletProvider {
abstract scan(): Wallet[] | Promise<Wallet[]>;

abstract readonly wallets$: Observable<Wallet[]>;
abstract getWallets(): Wallet[] | Promise<Wallet[]>;
}
19 changes: 1 addition & 18 deletions packages/react/src/hooks/use-wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
type Config,
getConnectedWallets,
} from "@reactive-dot/core";
import { Wallet, WalletProvider } from "@reactive-dot/core/wallets.js";
import { atom, useAtomValue } from "jotai";
import { atomWithObservable } from "jotai/utils";

Expand All @@ -27,28 +26,12 @@ export function useConnectedWallets() {
return useAtomValue(connectedWalletsAtom(useConfig()));
}

const providerWalletsAtom = atomFamilyWithErrorCatcher(
(config: Config, withErrorCatcher) =>
withErrorCatcher(atomWithObservable)(() =>
aggregateWallets(
config.wallets?.filter(
(walletOrProvider) => walletOrProvider instanceof WalletProvider,
) ?? [],
),
),
);

/**
* @internal
*/
export const walletsAtom = atomFamilyWithErrorCatcher(
(config: Config, withErrorCatcher) =>
withErrorCatcher(atom)(async (get) => [
...(config.wallets?.filter(
(walletOrProvider) => walletOrProvider instanceof Wallet,
) ?? []),
...(await get(providerWalletsAtom(config))),
]),
withErrorCatcher(atom)(() => aggregateWallets(config.wallets ?? [])),
);

/**
Expand Down
9 changes: 3 additions & 6 deletions packages/vue/src/composables/use-wallet-connector.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useAsyncAction } from "./use-async-action.js";
import { useWalletsObservable } from "./use-wallets.js";
import { useWalletsPromise } from "./use-wallets.js";
import { connectWallet } from "@reactive-dot/core";
import type { Wallet } from "@reactive-dot/core/wallets.js";
import { firstValueFrom } from "rxjs";

/**
* Composable for connecting wallets
Expand All @@ -13,13 +12,11 @@ import { firstValueFrom } from "rxjs";
export function useWalletConnector(wallets?: Wallet | Wallet[]) {
const composableWallets = wallets;

const walletsObservable = useWalletsObservable();
const walletsPromise = useWalletsPromise();

return useAsyncAction(async (wallets?: Wallet | Wallet[]) => {
const walletsToConnect =
wallets ??
composableWallets ??
(await firstValueFrom(walletsObservable.value));
wallets ?? composableWallets ?? (await walletsPromise.value);

await connectWallet(walletsToConnect);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/vue/src/composables/use-wallets-initializer.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { useAsyncAction } from "./use-async-action.js";
import { useWalletsObservable } from "./use-wallets.js";
import { useWalletsPromise } from "./use-wallets.js";
import { initializeWallets } from "@reactive-dot/core/wallets.js";
import { firstValueFrom } from "rxjs";

/**
* Composable for initializing wallets.
*
* @returns The initialization state and initialize function
*/
export function useWalletsInitializer() {
const walletsObservable = useWalletsObservable();
const walletsPromise = useWalletsPromise();

return useAsyncAction(async () =>
initializeWallets(await firstValueFrom(walletsObservable.value)),
initializeWallets(await walletsPromise.value),
);
}
21 changes: 4 additions & 17 deletions packages/vue/src/composables/use-wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,24 @@ import { useAsyncData } from "./use-async-data.js";
import { useConfig } from "./use-config.js";
import { useLazyValue } from "./use-lazy-value.js";
import { aggregateWallets, getConnectedWallets } from "@reactive-dot/core";
import { Wallet, WalletProvider } from "@reactive-dot/core/wallets.js";
import { map } from "rxjs/operators";

/**
* Composable for getting all available wallets.
*
* @returns Available wallets
*/
export function useWallets() {
return useAsyncData(useWalletsObservable());
return useAsyncData(useWalletsPromise());
}

/**
* @internal
*/
export function useWalletsObservable() {
export function useWalletsPromise() {
const config = useConfig();

return useLazyValue(["wallets"], () =>
aggregateWallets(
config.value.wallets?.filter(
(wallet) => wallet instanceof WalletProvider,
) ?? [],
).pipe(
map((aggregatedWallets) => [
...(config.value.wallets?.filter(
(wallet) => wallet instanceof Wallet,
) ?? []),
...aggregatedWallets,
]),
),
aggregateWallets(config.value.wallets ?? []),
);
}

Expand All @@ -50,6 +37,6 @@ export function useConnectedWallets() {
*/
export function useConnectedWalletsObservable() {
return useLazyValue(["connected-wallets"], () =>
getConnectedWallets(useWalletsObservable().value),
getConnectedWallets(useWalletsPromise().value),
);
}

0 comments on commit bbda9ef

Please sign in to comment.