-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Disable Hardware Authenticator identities for unsupported Neighborhoods #75
Changes from all commits
8548843
a4530dc
9d1c8e8
aa4d936
ff7747a
cab8c4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,39 +12,78 @@ | |
Tokens, | ||
} from "@liftedinit/many-js"; | ||
import { useAccountsStore } from "features/accounts"; | ||
import { createContext, ReactNode, useMemo } from "react"; | ||
import { | ||
createContext, | ||
ReactNode, | ||
useContext, | ||
useEffect, | ||
useMemo, | ||
useState, | ||
} from "react"; | ||
import { useNeighborhoodStore } from "./store"; | ||
|
||
export const NeighborhoodContext = createContext<Network | undefined>( | ||
undefined | ||
); | ||
interface INeighborhoodContext { | ||
query?: Network; | ||
command?: Network; | ||
services: Set<string>; | ||
} | ||
|
||
export const NeighborhoodContext = createContext<INeighborhoodContext>({ | ||
services: new Set(), | ||
}); | ||
|
||
export function NeighborhoodProvider({ children }: { children: ReactNode }) { | ||
const activeNeighborhood = useNeighborhoodStore( | ||
(s) => s.neighborhoods[s.activeId] | ||
); | ||
const activeAccount = useAccountsStore((s) => s.byId.get(s.activeId))!; | ||
|
||
const neighborhood = useMemo(() => { | ||
const identity = activeAccount?.identity ?? new AnonymousIdentity(); | ||
const network = new Network(activeNeighborhood.url, identity); | ||
network.apply([ | ||
Account, | ||
Base, | ||
Blockchain, | ||
Compute, | ||
Events, | ||
IdStore, | ||
KvStore, | ||
Ledger, | ||
Tokens, | ||
]); | ||
return network; | ||
}, [activeAccount, activeNeighborhood]); | ||
const { url } = useNeighborhoodStore((s) => s.neighborhoods[s.activeId]); | ||
const account = useAccountsStore((s) => s.byId.get(s.activeId))!; | ||
const [services, setServices] = useState<Set<string>>(new Set()); | ||
|
||
const context = useMemo(() => { | ||
const anonymous = new AnonymousIdentity(); | ||
const identity = account?.identity ?? anonymous; | ||
|
||
const query = new Network(url, anonymous); | ||
const command = new Network(url, identity); | ||
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating the two Networks here... |
||
[query, command].forEach((network) => | ||
network.apply([ | ||
Account, | ||
Base, | ||
Blockchain, | ||
Compute, | ||
Events, | ||
IdStore, | ||
KvStore, | ||
Ledger, | ||
Tokens, | ||
]) | ||
); | ||
return { query, command, services }; | ||
}, [account, url, services]); | ||
|
||
useEffect(() => { | ||
async function updateServices() { | ||
if (!context.query || !context.query.base) { | ||
return; | ||
} | ||
const updated = new Set<string>(); | ||
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 ( | ||
<NeighborhoodContext.Provider value={neighborhood}> | ||
<NeighborhoodContext.Provider value={context}> | ||
{children} | ||
</NeighborhoodContext.Provider> | ||
); | ||
} | ||
|
||
export const useNeighborhoodContext = () => useContext(NeighborhoodContext); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Finally, return a |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,7 @@ | |
AccountMultisigArgument, | ||
CreateAccountResponse, | ||
} from "@liftedinit/many-js"; | ||
import { NeighborhoodContext } from "api/neighborhoods"; | ||
import { useContext } from "react"; | ||
import { useNeighborhoodContext } from "api/neighborhoods"; | ||
import { useMutation } from "react-query"; | ||
import { accountLedgerFeature, accountMultisigFeature } from "../types"; | ||
|
||
|
@@ -20,7 +19,7 @@ | |
}; | ||
|
||
export function useCreateAccount() { | ||
const n = useContext(NeighborhoodContext); | ||
const { command: n } = useNeighborhoodContext(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change to context required a lot of changes. I approached them by changing as little code as possible, mostly destructuring using the existing variable name so I didn't have to touch the rest of the code. I had to decide whether the function needed the query or command network — mutations got command, queries got query. |
||
return useMutation<CreateAccountResponse, Error, CreateAccountFormData>( | ||
async (vars: CreateAccountFormData) => { | ||
const name = vars.accountSettings.name; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,39 @@ | ||
import React from "react"; | ||
fmorency marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { | ||
AnonymousIdentity, | ||
ANON_IDENTITY, | ||
WebAuthnIdentity, | ||
} from "@liftedinit/many-js"; | ||
import { useAccountsStore } from "features/accounts"; | ||
import { | ||
AddressText, | ||
Box, | ||
Button, | ||
ChevronDownIcon, | ||
Circle, | ||
EditIcon, | ||
Flex, | ||
HStack, | ||
Icon, | ||
IconButton, | ||
Menu, | ||
MenuButton, | ||
MenuList, | ||
MenuDivider, | ||
MenuItem, | ||
MenuList, | ||
MenuOptionGroup, | ||
MenuDivider, | ||
SimpleGrid, | ||
Text, | ||
UsbIcon, | ||
useDisclosure, | ||
VStack, | ||
AddressText, | ||
ChevronDownIcon, | ||
EditIcon, | ||
UserIcon, | ||
UsbIcon, | ||
useToast, | ||
VStack, | ||
} from "@liftedinit/ui"; | ||
import { useNeighborhoodContext } from "api/neighborhoods"; | ||
import { useAccountsStore } from "features/accounts"; | ||
import { useEffect, useState } from "react"; | ||
import { Account, AccountId } from "../../types"; | ||
import { AddAccountModal } from "./add-account-modal"; | ||
import { EditAccountModal } from "./edit-account-modal"; | ||
import { Account, AccountId } from "../../types"; | ||
|
||
export type AccountItemWithIdDisplayStrings = [ | ||
AccountId, | ||
|
@@ -66,7 +68,25 @@ | |
}) | ||
); | ||
|
||
const [editAccount, setEditAccount] = React.useState< | ||
const toast = useToast(); | ||
const { services } = useNeighborhoodContext(); | ||
useEffect(() => { | ||
(async () => { | ||
const isWebAuthnIdentity = | ||
activeAccount?.identity instanceof WebAuthnIdentity; | ||
if (isWebAuthnIdentity && !services.has("idstore")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Getting the list of services is no longer asynchronous, which I suspect was causing a problem here. |
||
setActiveId(0); // reset to Anonymous | ||
toast({ | ||
status: "warning", | ||
title: "Unsupported Identity", | ||
description: | ||
"Selected Neighborhood does not support Hardware Authenticators", | ||
}); | ||
} | ||
})(); | ||
}, [activeAccount, services, setActiveId, toast]); | ||
|
||
const [editAccount, setEditAccount] = useState< | ||
[number, Account] | undefined | ||
>(); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of returning just a Network (using the active Identity), the context is now a little more complex with a Network for queries (anonymous) and one for commands (using the active Identity). Also, the services are computed once and stored in the context, instead of queried at the component level.