diff --git a/src/features/accounts/components/accounts-menu/accounts-menu.tsx b/src/features/accounts/components/accounts-menu/accounts-menu.tsx
index 70f542c..01be66a 100644
--- a/src/features/accounts/components/accounts-menu/accounts-menu.tsx
+++ b/src/features/accounts/components/accounts-menu/accounts-menu.tsx
@@ -1,4 +1,4 @@
-import React from "react"
+import { useEffect, useState } from "react"
import {
AnonymousIdentity,
ANON_IDENTITY,
@@ -26,10 +26,12 @@ import {
useDisclosure,
VStack,
UsbIcon,
+ useToast,
} from "@liftedinit/ui"
import { AddAccountModal } from "./add-account-modal"
import { EditAccountModal } from "./edit-account-modal"
import { Account, AccountId } from "../../types"
+import { useNetworkContext } from "features/network"
export type AccountItemWithIdDisplayStrings = [
AccountId,
@@ -64,10 +66,28 @@ export function AccountsMenu() {
}),
)
- const [editAccount, setEditAccount] = React.useState<
+ const [editAccount, setEditAccount] = useState<
[number, Account] | undefined
>()
+ const toast = useToast()
+ const { services } = useNetworkContext()
+ useEffect(() => {
+ ; (async () => {
+ const isWebAuthnIdentity =
+ activeAccount?.identity instanceof WebAuthnIdentity
+ if (isWebAuthnIdentity && !services.has("idstore")) {
+ setActiveId(0) // reset to Anonymous
+ toast({
+ status: "warning",
+ title: "Unsupported Identity",
+ description:
+ "Selected Neighborhood does not support Hardware Authenticators",
+ })
+ }
+ })()
+ }, [activeAccount, services, setActiveId, toast])
+
function onEditClick(acct: [number, Account]) {
setEditAccount(acct)
onEditModalOpen()
diff --git a/src/features/accounts/components/accounts-menu/add-account-modal.tsx b/src/features/accounts/components/accounts-menu/add-account-modal.tsx
index 0b7800b..43e7251 100644
--- a/src/features/accounts/components/accounts-menu/add-account-modal.tsx
+++ b/src/features/accounts/components/accounts-menu/add-account-modal.tsx
@@ -10,6 +10,7 @@ import {
Tabs,
VStack,
} from "@liftedinit/ui"
+import { useNetworkContext } from "features/network"
import React from "react"
import { SocialLogin } from "../social-login"
@@ -87,13 +88,13 @@ export function AddAccountModal({
)}
{(addMethod === AddAccountMethodTypes.importAuthenticator ||
addMethod === AddAccountMethodTypes.createAuthenticator) && (
-
- )}
+
+ )}
{showDefaultFooter && (
@@ -164,6 +165,7 @@ const createCards = [
label: "Hardware Authenticator",
title: "create new with hardware authenticator",
onClickArg: AddAccountMethodTypes.createAuthenticator,
+ requires: "idstore",
},
]
@@ -172,18 +174,21 @@ function CreateAccountOptions({
}: {
onAddMethodClick: (method: AddAccountMethodTypes) => void
}) {
+ const { services } = useNetworkContext()
return (
- {createCards.map((c, idx) => {
- return (
- onAddMethodClick(c.onClickArg)}
- />
- )
- })}
+ {createCards
+ .filter(c => !c.requires || services.has(c.requires))
+ .map((c, idx) => {
+ return (
+ onAddMethodClick(c.onClickArg)}
+ />
+ )
+ })}
)
}
@@ -203,6 +208,7 @@ const importCards = [
label: "Hardware Authenticator",
title: "import with hardware authenticator",
onClickArg: AddAccountMethodTypes.importAuthenticator,
+ requires: "idstore",
},
]
function ImportAcountOptions({
@@ -210,18 +216,21 @@ function ImportAcountOptions({
}: {
onAddMethodClick: (method: AddAccountMethodTypes) => void
}) {
+ const { services } = useNetworkContext()
return (
- {importCards.map((c, idx) => {
- return (
- onAddMethodClick(c.onClickArg)}
- />
- )
- })}
+ {importCards
+ .filter(c => !c.requires || services.has(c.requires))
+ .map((c, idx) => {
+ return (
+ onAddMethodClick(c.onClickArg)}
+ />
+ )
+ })}
)
}
diff --git a/src/features/network/network-provider.tsx b/src/features/network/network-provider.tsx
index 6b34674..8da4943 100644
--- a/src/features/network/network-provider.tsx
+++ b/src/features/network/network-provider.tsx
@@ -1,4 +1,11 @@
-import React, { createContext, ReactNode, useContext, useMemo } from "react"
+import {
+ createContext,
+ ReactNode,
+ useContext,
+ useEffect,
+ useMemo,
+ useState,
+} from "react"
import {
Network,
Ledger,
@@ -15,14 +22,18 @@ export interface INetworkContext {
query?: Network
command?: Network
legacy?: Network[] // Legacy networks are past networks that are no longer active. They are used to query for past events.
+ services: Set
}
-export const NetworkContext = createContext({})
+export const NetworkContext = createContext({
+ services: new Set(),
+})
export function NetworkProvider({ children }: { children: ReactNode }) {
const network = useNetworkStore().getActiveNetwork()
const legacy_networks = useNetworkStore().getLegacyNetworks()
const account = useAccountsStore(s => s.byId.get(s.activeId))
+ const [services, setServices] = useState>(new Set())
const url = network.url
const legacy_urls = legacy_networks?.map(n => n.url)
@@ -43,8 +54,28 @@ export function NetworkProvider({ children }: { children: ReactNode }) {
const command = new Network(url, identity)
command.apply([Ledger, IdStore, Account])
- return { query, command, legacy }
- }, [account, url, legacy_urls])
+ return { query, command, legacy, services }
+ }, [account, url, legacy_urls, services])
+
+ useEffect(() => {
+ async function updateServices() {
+ if (!context.query || !context.query.base) {
+ return
+ }
+ const updated = new Set()
+ try {
+ const { endpoints } = await context.query.base.endpoints()
+ endpoints
+ .map((endpoint: string) => endpoint.split(".")[0])
+ .forEach((service: string) => updated.add(service))
+ } catch (error) {
+ console.error(`Couldn't update services: ${(error as Error).message}`)
+ }
+ setServices(updated)
+ }
+ updateServices()
+ // eslint-disable-next-line
+ }, [url])
return (