Skip to content

Commit

Permalink
feat: add organization dropdown to dfns modal
Browse files Browse the repository at this point in the history
  • Loading branch information
Polybius93 committed Dec 4, 2024
1 parent ada07dd commit 1c807ea
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { Button, FormControl, FormErrorMessage, FormLabel, Input, VStack } from '@chakra-ui/react';
import { DFNSCustomerConfiguration } from '@models/configuration';
import { useForm } from '@tanstack/react-form';

interface DFNSModalRegisterFormProps {
onSubmit: (credentialCode: string) => Promise<void>;
onSubmit: (
credentialCode: string,
selectedDFNSOrganization: DFNSCustomerConfiguration
) => Promise<void>;
selectedDFNSOrganization: DFNSCustomerConfiguration;
}

export function DFNSModalRegisterForm({ onSubmit }: DFNSModalRegisterFormProps): React.JSX.Element {
export function DFNSModalRegisterForm({
onSubmit,
selectedDFNSOrganization,
}: DFNSModalRegisterFormProps): React.JSX.Element {
const formAPI = useForm({
defaultValues: {
credentialCode: '',
},
onSubmit: async ({ value }) => {
await onSubmit(value.credentialCode);
await onSubmit(value.credentialCode, selectedDFNSOrganization);
},
});

Expand All @@ -36,7 +44,9 @@ export function DFNSModalRegisterForm({ onSubmit }: DFNSModalRegisterFormProps):
}}
children={field => (
<FormControl isInvalid={!!field.state.meta.errorMap.onChange}>
<FormLabel>Credential Code</FormLabel>
<FormLabel color={'white.01'} fontSize={'md'}>
Credential Code
</FormLabel>
<Input
type="string"
value={field.state.value}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Button, FormControl, FormErrorMessage, FormLabel, Input, VStack } from '@chakra-ui/react';
import { DFNSCustomerConfiguration } from '@models/configuration';
import { useForm } from '@tanstack/react-form';

interface DFNSModalLoginFormProps {
onSubmit: (email: string) => Promise<void>;
onSubmit: (email: string, selectedDFNSOrganization: DFNSCustomerConfiguration) => Promise<void>;
selectedDFNSOrganization: DFNSCustomerConfiguration;
}

const validateEmail = (email: string) => {
Expand All @@ -18,13 +20,16 @@ const validateEmail = (email: string) => {
return undefined;
};

export function DFNSModalLoginForm({ onSubmit }: DFNSModalLoginFormProps): React.JSX.Element {
export function DFNSModalLoginForm({
onSubmit,
selectedDFNSOrganization,
}: DFNSModalLoginFormProps): React.JSX.Element {
const formAPI = useForm({
defaultValues: {
email: '',
},
onSubmit: async ({ value }) => {
await onSubmit(value.email);
await onSubmit(value.email, selectedDFNSOrganization);
},
});

Expand All @@ -45,7 +50,9 @@ export function DFNSModalLoginForm({ onSubmit }: DFNSModalLoginFormProps): React
}}
children={field => (
<FormControl isInvalid={!!field.state.meta.errorMap.onChange}>
<FormLabel>E-Mail Address</FormLabel>
<FormLabel color={'white.01'} fontSize={'md'}>
E-Mail Address
</FormLabel>
<Input
type="email"
value={field.state.value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function DFNSModalLayout({
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Image src={logo} alt={'DFNS Logo'} boxSize={'150px'} objectFit={'contain'} />
<Image src={logo} alt={'DFNS Logo'} boxSize={'100px'} objectFit={'contain'} />
</ModalHeader>
<ModalCloseButton />
<ModalBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ChevronDownIcon } from '@chakra-ui/icons';
import { HStack, Menu, MenuButton, MenuItem, MenuList, Text, VStack } from '@chakra-ui/react';
import { DFNSCustomerConfiguration } from '@models/configuration';

interface DFNSModalSelectOrganizationMenuProps {
handleChangeOrganization: (dfnsOrganization: DFNSCustomerConfiguration) => void;
dfnsOrganizations: DFNSCustomerConfiguration[];
selectedDFNSOrganization: DFNSCustomerConfiguration;
}

export function DFNSModalSelectOrganizationMenu({
handleChangeOrganization,
dfnsOrganizations,
selectedDFNSOrganization,
}: DFNSModalSelectOrganizationMenuProps): React.JSX.Element {
return (
<VStack w={'100%'}>
<Text fontSize={'md'} color={'white'} w={'full'}>
Organization
</Text>
<Menu variant={'organization'}>
<MenuButton>
<HStack justifyContent={'space-between'}>
<Text>{selectedDFNSOrganization.name}</Text>
<ChevronDownIcon color={'white'} />
</HStack>
</MenuButton>
<MenuList>
{dfnsOrganizations.map(dfnsOrganization => {
return (
<MenuItem
key={dfnsOrganization.name}
value={dfnsOrganization.name}
onClick={() => handleChangeOrganization(dfnsOrganization)}
>
{dfnsOrganization.name}
</MenuItem>
);
})}
</MenuList>
</Menu>
</VStack>
);
}
41 changes: 31 additions & 10 deletions src/app/components/modals/dfns-modal/dfns-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useContext, useState } from 'react';

import { Collapse } from '@chakra-ui/react';
import { Collapse, VStack } from '@chakra-ui/react';
import { useDFNS } from '@hooks/use-dfns';
import { DFNSCustomerConfiguration } from '@models/configuration';
import {
BitcoinWalletContext,
BitcoinWalletContextState,
Expand All @@ -16,6 +17,7 @@ import { DFNSModalLayout } from './components/dfns-modal-layout';
import { DFNSModalLoadingStack } from './components/dfns-modal-loading-stack';
import { DFNSModalNavigatorButton } from './components/dfns-modal-navigator-button-stack';
import { DFNSModalSelectAddressMenu } from './components/dfns-modal-select-address-menu/dfns-modal-select-address-menu';
import { DFNSModalSelectOrganizationMenu } from './components/dfns-modal-select-organization-menu';
import { DFNSModalSuccessIcon } from './components/dfns-modal-success-icon';

export function DFNSModal({ isOpen, handleClose }: ModalComponentProps): React.JSX.Element {
Expand All @@ -26,6 +28,9 @@ export function DFNSModal({ isOpen, handleClose }: ModalComponentProps): React.J
const [taprootAddresses, setTaprootAddresses] = useState<
{ address: string | undefined; walletID: string }[] | undefined
>(undefined);
const [organization, setOrganization] = useState<DFNSCustomerConfiguration>(
appConfiguration.dfnsConfiguration.dfnsCustomerConfigurations[0]
);

const [isSuccesful, setIsSuccesful] = useState(false);
const [isRegister, setIsRegister] = useState(false);
Expand All @@ -45,19 +50,22 @@ export function DFNSModal({ isOpen, handleClose }: ModalComponentProps): React.J
setDFNSError(undefined);
}

async function connectDFNSWalletAndGetAddresses(username: string) {
async function connectDFNSWalletAndGetAddresses(
username: string,
organization: DFNSCustomerConfiguration
) {
try {
const taprootAddresses = await connectDFNSWallet(username);
const taprootAddresses = await connectDFNSWallet(username, organization);
setTaprootAddresses(taprootAddresses);
setIsLoadingAddressList(false);
} catch (error: any) {
await setError(error.message);
}
}

async function handleRegisterCredentials(code: string) {
async function handleRegisterCredentials(code: string, organization: DFNSCustomerConfiguration) {
try {
await registerCredentials(code);
await registerCredentials(code, organization);
setIsRegister(false);
} catch (error: any) {
await setError(error.message);
Expand All @@ -81,11 +89,24 @@ export function DFNSModal({ isOpen, handleClose }: ModalComponentProps): React.J
<DFNSModalLayout logo={'/images/logos/dfns-logo.svg'} isOpen={isOpen} onClose={handleClose}>
<DFNSModalSuccessIcon isSuccesful={isSuccesful} />
<Collapse in={!taprootAddresses} layout style={{ width: '100%' }}>
{!isRegister ? (
<DFNSModalLoginForm onSubmit={connectDFNSWalletAndGetAddresses} />
) : (
<DFNSModalRegisterForm onSubmit={handleRegisterCredentials} />
)}
<VStack spacing={4} w={'100%'}>
<DFNSModalSelectOrganizationMenu
dfnsOrganizations={appConfiguration.dfnsConfiguration.dfnsCustomerConfigurations}
selectedDFNSOrganization={organization}
handleChangeOrganization={setOrganization}
/>
{!isRegister ? (
<DFNSModalLoginForm
onSubmit={connectDFNSWalletAndGetAddresses}
selectedDFNSOrganization={organization}
/>
) : (
<DFNSModalRegisterForm
onSubmit={handleRegisterCredentials}
selectedDFNSOrganization={organization}
/>
)}
</VStack>
</Collapse>
<DFNSModalLoadingStack isLoading={isLoading} />
<DFNSModalSelectAddressMenu
Expand Down
27 changes: 13 additions & 14 deletions src/app/hooks/use-dfns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useContext, useState } from 'react';

import { DfnsApiClient, DfnsAuthenticator } from '@dfns/sdk';
import { WebAuthnSigner } from '@dfns/sdk-browser';
import { DFNSCustomerConfiguration } from '@models/configuration';
import { BitcoinWalletType } from '@models/wallet';
import { BitcoinWalletContext } from '@providers/bitcoin-wallet-context-provider';
import { DFNSDLCHandler } from 'dlc-btc-lib';
Expand All @@ -19,9 +20,13 @@ const BITCOIN_NETWORK_DFNS_MAP = {

interface UseDFNSReturnType {
connectDFNSWallet: (
userName: string
userName: string,
dfnsConfiguration: DFNSCustomerConfiguration
) => Promise<{ address: string | undefined; walletID: string }[]>;
registerCredentials: (code: string) => Promise<void>;
registerCredentials: (
code: string,
dfnsConfiguration: DFNSCustomerConfiguration
) => Promise<void>;
selectWallet: (walletId: string) => Promise<void>;
handleFundingTransaction: (
dlcHandler: DFNSDLCHandler,
Expand Down Expand Up @@ -53,19 +58,13 @@ export function useDFNS(): UseDFNSReturnType {
const [isLoading, setIsLoading] = useState<[boolean, string]>([false, '']);

async function connectDFNSWallet(
userName: string
userName: string,
dfnsConfiguration: DFNSCustomerConfiguration
): Promise<{ address: string | undefined; walletID: string }[]> {
try {
setIsLoading([true, 'Connecting to DFNS Wallet']);

const { dfnsBaseURL } = appConfiguration.dfnsConfiguration;
const dfnsConfiguration = appConfiguration.dfnsConfiguration.dfnsCustomerConfigurations.find(
({ name }) => name === 'Tungsten'
);

if (!dfnsConfiguration) {
throw new Error('DFNS Configuration not found');
}

const { applicationID, organizationID } = dfnsConfiguration;

Expand Down Expand Up @@ -124,11 +123,11 @@ export function useDFNS(): UseDFNSReturnType {
}
}

async function registerCredentials(code: string): Promise<void> {
async function registerCredentials(
code: string,
dfnsConfiguration: DFNSCustomerConfiguration
): Promise<void> {
const { dfnsBaseURL } = appConfiguration.dfnsConfiguration;
const dfnsConfiguration = appConfiguration.dfnsConfiguration.dfnsCustomerConfigurations.find(
({ name }) => name === 'Tungsten'
);

if (!dfnsConfiguration) {
throw new Error('DFNS Configuration not found');
Expand Down
9 changes: 1 addition & 8 deletions src/app/hooks/use-leather.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ export function useLeather(): UseLeatherReturnType {
const rpcResponse: RpcResponse = await window.btc?.request('getAddresses');
const userAddresses = rpcResponse.result.addresses;

console.log('userAddresses', userAddresses);

checkUserWalletNetwork(userAddresses[0]);

const bitcoinAddresses = userAddresses.filter(
Expand Down Expand Up @@ -250,15 +248,10 @@ export function useLeather(): UseLeatherReturnType {
feeRateMultiplier
);

console.log('withdrawalTransaction', withdrawalTransaction);

setIsLoading([true, 'Sign Withdrawal Transaction in your Leather Wallet']);
// ==> Sign Withdrawal PSBT with Ledger
const withdrawalTransactionHex = await signPSBT(withdrawalTransaction.toPSBT());
console.log(
'withdrawalTransactionHex',
Transaction.fromPSBT(hexToBytes(withdrawalTransactionHex))
);

setIsLoading([false, '']);
return withdrawalTransactionHex;
} catch (error) {
Expand Down
2 changes: 0 additions & 2 deletions src/app/hooks/use-ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,6 @@ export function useLedger(): UseLedgerReturnType {
feeRateMultiplier
);

console.log('withdrawalPSBT', withdrawalPSBT);

setIsLoading([true, 'Sign Withdrawal Transaction in your Leather Wallet']);
// ==> Sign Withdrawal PSBT with Ledger
const withdrawalTransaction = await dlcHandler.signPSBT(withdrawalPSBT, 'withdraw');
Expand Down
2 changes: 1 addition & 1 deletion src/app/store/slices/modal/modal.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const initialModalState: ModalState = {
isSuccesfulFlowModalOpen: [false, undefined, '', 'mint', 0],
isSelectBitcoinWalletModalOpen: false,
isLedgerModalOpen: false,
isDFNSModalOpen: false,
isDFNSModalOpen: true,
};

export const modalSlice = createSlice({
Expand Down
2 changes: 1 addition & 1 deletion src/shared/models/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ enum AppEnvironment {
LOCALHOST = 'localhost',
}

interface DFNSCustomerConfiguration {
export interface DFNSCustomerConfiguration {
name: string;
organizationID: string;
applicationID: string;
Expand Down
40 changes: 40 additions & 0 deletions src/styles/menu-theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,45 @@ const network = definePartsStyle({
},
});

const organization = definePartsStyle({
button: {
justifyContent: 'center',
p: '10px',
h: '50px',
w: '375px',
bg: 'transparent',
border: '1px solid',
borderColor: 'white.01',
borderRadius: 'md',
color: 'white',
fontSize: 'sm',
fontWeight: 600,
_hover: {
background: 'white.03',
},
},
list: {
p: '10px',
w: '375px',
bgColor: '#170C33',
border: '1.5px solid',
borderColor: 'border.white.01',
borderRadius: 'md',
},
item: {
justifyContent: 'center',
bgColor: 'inherit',
borderRadius: 'md',
color: 'white',
fontSize: 'xs',
fontWeight: 400,
_hover: {
background: 'white.03',
},
transition: 'all 0.05s ease-in-out',
},
});

const ledgerAddress = definePartsStyle({
button: {
justifyContent: 'center',
Expand Down Expand Up @@ -184,6 +223,7 @@ const variants = {
account,
ledgerAddress,
networkChange,
organization,
};

export const menuTheme = defineMultiStyleConfig({ sizes, variants });

0 comments on commit 1c807ea

Please sign in to comment.