refactor: move Sign modal accounts to new state

This commit is contained in:
Victor Oliva
2025-08-31 08:02:22 +08:00
parent 9415597695
commit 997a70f0bb
5 changed files with 66 additions and 95 deletions
+7 -6
View File
@@ -1,7 +1,12 @@
import { AccountIdDisplay } from "@/components/AccountIdDisplay"
import { TokenAmount } from "@/components/TokenAmount"
import { Button } from "@/components/ui/button"
import { Account, accounts$, AccountSource } from "@/state/accounts.state"
import {
Account,
accounts$,
AccountSource,
accountSourceTypeToName,
} from "@/state/accounts.state"
import { chainProperties$ } from "@/state/chain-props.state"
import { client$ } from "@/state/chains/chain.state"
import { MultiAddress, polkadot_people } from "@polkadot-api/descriptors"
@@ -56,10 +61,6 @@ const sourceColors: Record<AccountSource, string> = {
extension: "var(--color-orange-800)",
walletconnect: "var(--color-blue-800)",
}
const typeToName: Record<AccountSource, string> = {
extension: "Extension",
walletconnect: "Wallet Connect",
}
const getExtensionName = (id: string) => knownExtensions[id]?.name ?? id
const SourceTag: FC<{
@@ -75,7 +76,7 @@ const SourceTag: FC<{
>
{account.type === "extension"
? getExtensionName(account.extensionId)
: typeToName[account.type]}
: accountSourceTypeToName[account.type]}
</div>
)
}
@@ -9,18 +9,18 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { groupBy } from "@/lib/groupBy"
import { knownExtensions } from "@/pages/Accounts/Providers"
import {
accountsByExtension$,
extensionAccounts$,
selectedExtensions$,
} from "@/state/extension-accounts.state"
import {
walletConnectAccounts$,
walletConnectStatus$,
} from "@/state/walletconnect.state"
import { state, useStateObservable } from "@react-rxjs/core"
Account,
accounts$,
AccountSource,
accountSourceTypeToName,
} from "@/state/accounts.state"
import { selectedExtensions$ } from "@/state/extension-accounts.state"
import { walletConnectStatus$ } from "@/state/walletconnect.state"
import { state, useStateObservable, withDefault } from "@react-rxjs/core"
import { createSignal } from "@react-rxjs/utils"
import { InjectedExtension } from "polkadot-api/pjs-signer"
import React from "react"
import {
combineLatest,
@@ -32,51 +32,6 @@ import {
tap,
} from "rxjs"
const Accounts: React.FC<{ extension: InjectedExtension }> = ({
extension,
}) => {
const accounts = useStateObservable(extensionAccounts$(extension.name))
return (
<SelectGroup>
<SelectLabel>{extension.name}</SelectLabel>
{accounts.map((account) => (
<SelectItem
key={account.address}
value={account.address + "-" + extension.name}
>
{account.address.startsWith("0x") ? (
<EthAccountDisplay value={account.address} />
) : (
<AccountIdDisplay value={account.address} />
)}
</SelectItem>
))}
</SelectGroup>
)
}
const WalletConnectAccounts = () => {
const accounts = useStateObservable(walletConnectAccounts$)
if (!Object.keys(accounts).length) return null
return (
<SelectGroup>
<SelectLabel className="flex gap-1">Wallet Connect</SelectLabel>
{Object.keys(accounts).map((address) => (
<SelectItem key={address} value={address + "-" + "wallet_connect"}>
{address.startsWith("0x") ? (
<EthAccountDisplay value={address} />
) : (
<AccountIdDisplay value={address} />
)}
</SelectItem>
))}
</SelectGroup>
)
}
const [valueSelected$, selectValue] = createSignal<string>()
const LS_KEY = "selected-signer"
@@ -88,54 +43,53 @@ const selectedValue$ = state(
null,
)
const allAccounts$ = accounts$.pipeState(
map((v) =>
v.map((acc) => ({
...acc,
id: `${acc.accountId}-${acc.type === "extension" ? acc.extensionId : acc.type}`,
})),
),
withDefault([]),
)
export const selectedAccount$ = state(
combineLatest([
selectedValue$,
accountsByExtension$,
walletConnectAccounts$,
]).pipe(
map(([selectedAccount, accountsByExtension, walletConnectAccounts]) => {
combineLatest([selectedValue$, allAccounts$]).pipe(
map(([selectedAccount, accounts]): Account | null => {
if (!selectedAccount) return null
const [address, ...rest] = selectedAccount.split("-")
const signer = rest.join("-")
const account = accounts.find((account) => account.id === selectedAccount)
if (signer === "wallet_connect") {
return address in walletConnectAccounts
? {
polkadotSigner: walletConnectAccounts[address],
}
: null
}
const accounts = accountsByExtension.get(signer)
if (!accounts) return null
return accounts.find((account) => account.address === address) ?? null
return account ?? null
}),
distinctUntilChanged(),
),
null,
)
const allAccounts$ = state(
combineLatest([walletConnectAccounts$, accountsByExtension$]).pipe(
map(([wcAccounts, accounts]) => [
...Object.keys(wcAccounts).map((account) => `${account}-wallet_connect`),
...[...accounts.entries()].flatMap(([extension, accounts]) =>
accounts.map((account) => `${account.address}-${extension}`),
),
]),
const groupedAccounts$ = allAccounts$.pipeState(
map((v) =>
groupBy(v, (acc) =>
acc.type === "extension" ? acc.extensionId : acc.type,
),
),
[],
)
const groupIdToName = (groupId: string) => {
if (groupId in knownExtensions) {
return knownExtensions[groupId].name
}
return accountSourceTypeToName[groupId as AccountSource] ?? groupId
}
export const AccountProvider: React.FC = () => {
const value = useStateObservable(selectedValue$)
const extensions = useStateObservable(selectedExtensions$)
const walletConnect = useStateObservable(walletConnectStatus$)
const allAccounts = useStateObservable(allAccounts$)
const groupedAccounts = useStateObservable(groupedAccounts$)
const activeExtensions = [...extensions.values()].filter((v) => !!v)
const valueExists = value && allAccounts.includes(value)
const valueExists = value && allAccounts.some((acc) => acc.id === value)
if (!activeExtensions.length && walletConnect.type !== "connected")
return null
@@ -149,10 +103,22 @@ export const AccountProvider: React.FC = () => {
<SelectValue placeholder="Select an account" />
</SelectTrigger>
<SelectContent>
{activeExtensions.map((extension) => (
<Accounts key={extension.name} extension={extension} />
{Object.entries(groupedAccounts).map(([groupId, accounts]) => (
<SelectGroup key={groupId}>
<SelectLabel className="flex gap-1">
{groupIdToName(groupId)}
</SelectLabel>
{accounts.map((account) => (
<SelectItem key={account.id} value={account.id}>
{account.accountId.startsWith("0x") ? (
<EthAccountDisplay value={account.accountId} />
) : (
<AccountIdDisplay value={account.accountId} />
)}
</SelectItem>
))}
</SelectGroup>
))}
<WalletConnectAccounts />
</SelectContent>
</Select>
)
@@ -26,7 +26,7 @@ const SignAndSubmit: FC<{ callData: string; onClose: () => void }> = ({
try {
const unsafeApi = await firstValueFrom(unsafeApi$)
const tx = await unsafeApi.txFromCallData(Binary.fromHex(callData))
const signedExtrinsic = await tx.sign(account!.polkadotSigner)
const signedExtrinsic = await tx.sign(account!.signer)
trackSignedTx(signedExtrinsic)
onClose()
} catch (ex) {
+4
View File
@@ -5,6 +5,10 @@ import { combineLatest, defer, from, map, switchMap } from "rxjs"
import { accountsByExtension$ } from "./extension-accounts.state"
export type AccountSource = "extension" | "walletconnect"
export const accountSourceTypeToName: Record<AccountSource, string> = {
extension: "Extension",
walletconnect: "Wallet Connect",
}
const lazyWalletConnectAccounts$ = defer(() =>
from(import("@/state/walletconnect.state")).pipe(
+1 -1
View File
@@ -157,7 +157,7 @@ export const selectedExtensions$ = extensions$.pipeState(
withDefault(new Map<string, InjectedExtension | null>()),
)
export const extensionAccounts$ = state(
const extensionAccounts$ = state(
(name: string) =>
extension$(name).pipe(
switchMap((x) => {