Skip to content
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/refactor ux #213

Merged
merged 15 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 65 additions & 73 deletions apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
} from '@/components/ui/dialog'
import { getTokenSymbol } from '@/helpers/getTokenInfo'
import { StrategyMethod, useStrategyCallback } from '@/hooks/useStrategy'
import { getDefaultStrategies, pushAmount, pushAsset } from '@/store/lib/features/vaultStore'
import { useAppDispatch } from '@/store/lib/storeHooks'
import { getDefaultStrategies, pushAmount, pushAsset, setAmountByAddress } from '@/store/lib/features/vaultStore'
import { useAppDispatch, useAppSelector } from '@/store/lib/storeHooks'
import { Asset, Strategy } from '@/store/lib/types'
import {
Button,
Expand Down Expand Up @@ -42,15 +42,36 @@ function AddNewStrategyButton() {
const { activeChain } = useSorobanReact()
const strategyCallback = useStrategyCallback();
const [open, setOpen] = useState<boolean>(false)
const [isLoading, setIsLoading] = useState<boolean>(false)
const [defaultStrategies, setDefaultStrategies] = useState<Strategy[]>([])
const [asset, setAsset] = useState<Asset>({ address: '', strategies: [] })
const newVault = useAppSelector((state) => state.newVault)
const [defaultStrategies, setDefaultStrategies] = useState<any[]>([])
const [selectedAsset, setSelectedAsset] = useState<Asset>({ address: '', strategies: [], symbol: '' })
const [assets, setAssets] = useState<Asset[]>([])
const [amountInput, setAmountInput] = useState<AmountInputProps>({ amount: 0, enabled: false })

const resetForm = () => {
setSelectedAsset({ address: '', strategies: [], symbol: '' })
setAmountInput({ amount: 0, enabled: false })
setOpen(false)
}

const getSymbol = async (address: string) => {
const symbol = await getTokenSymbol(address, sorobanContext)
if (!symbol) return '';
return symbol === 'native' ? 'XLM' : symbol
}

useEffect(() => {
const fetchStrategies = async () => {
const tempStrategies = await getDefaultStrategies(activeChain?.name?.toLowerCase() || 'testnet')
for (const strategy of tempStrategies) {
setDefaultStrategies(tempStrategies)
}
fetchStrategies();
}, [activeChain?.networkPassphrase])

useEffect(() => {
const fetchStrategies = async () => {
const rawDefaultStrategies = await getDefaultStrategies(activeChain?.name?.toLowerCase() || 'testnet')
const defaultStrategiesWithAssets = await Promise.all(rawDefaultStrategies.map(async (strategy) => {
const assetAddress = await strategyCallback(
strategy.address,
StrategyMethod.ASSET,
Expand All @@ -62,76 +83,23 @@ function AddNewStrategyButton() {
return asset;
})
const assetSymbol = await getSymbol(assetAddress)
setAsset({ ...asset, address: assetAddress, symbol: assetSymbol! })
const asset = { address: assetAddress, strategies: [strategy], symbol: assetSymbol! }
return asset
}
setDefaultStrategies(tempStrategies)
))
setAssets(defaultStrategiesWithAssets)
}
fetchStrategies();
}, [activeChain?.networkPassphrase])


const resetForm = () => {
setAsset({ address: '', strategies: [] })
setAmountInput({ amount: 0, enabled: false })
setOpen(false)
}

const getSymbol = async (address: string) => {
const symbol = await getTokenSymbol(address, sorobanContext)
if (!symbol) return '';
return symbol === 'native' ? 'XLM' : symbol
}

const handleSelectStrategy = (value: boolean, strategy: Strategy) => {
setIsLoading(true)
switch (value) {
case true:
const fetchAssets = async () => {
try {
const asset = await strategyCallback(
strategy.address,
StrategyMethod.ASSET,
undefined,
false
).then((result) => {
const resultScval = result as xdr.ScVal;
const asset = scValToNative(resultScval);
return asset;
});
const symbol = await getSymbol(asset);
const newAsset = { address: asset, symbol: symbol!, strategies: [strategy] }
console.log(newAsset)
setAsset({ address: asset, symbol: symbol!, strategies: [strategy] })
} catch (error) {
console.error(error);
} finally {
setIsLoading(false)
}
};
fetchAssets();
break
case false:
setAsset({ ...asset, strategies: asset.strategies.filter(str => str.address !== strategy.address) })
setIsLoading(false)
break
const selectedAsset = assets.find((asset) => asset.strategies.some((str) => str.address === strategy.address))
if (selectedAsset) {
setSelectedAsset(selectedAsset)
}
}

const addAsset = async () => {
const newAsset: Asset = {
address: asset.address,
strategies: asset.strategies,
symbol: asset.symbol
}
await dispatch(pushAsset(newAsset))
if (amountInput.enabled && amountInput.amount! > 0) {
await dispatch(pushAmount(amountInput.amount!))
}
resetForm()
}



const handleAmountInput = async (e: any) => {
const input = e.target.value
const decimalRegex = /^(\d+)?(\.\d{0,7})?$/
Expand All @@ -142,6 +110,31 @@ function AddNewStrategyButton() {
}
setAmountInput({ amount: input, enabled: true });
}
const strategyExists = (strategy: Strategy) => {
const exists = newVault.assets.some((asset) => asset.strategies.some((str) => str.address === strategy.address))
return exists
}
const addAsset = async () => {
const newAsset: Asset = {
address: selectedAsset.address,
strategies: selectedAsset.strategies,
symbol: selectedAsset.symbol
}
const exists = strategyExists(selectedAsset.strategies[0]!)
if (exists) {
if (amountInput.enabled && amountInput.amount! > 0) {
await dispatch(setAmountByAddress({ address: selectedAsset.address, amount: amountInput.amount }))
} else if (amountInput.enabled == false || amountInput.amount! == 0) {
await dispatch(setAmountByAddress({ address: selectedAsset.address, amount: 0 }))
}
}
await dispatch(pushAsset(newAsset))
if (!exists && amountInput.enabled && amountInput.amount! > 0) {
await dispatch(pushAmount(amountInput.amount!))
}
resetForm()
}

return (
<DialogRoot open={open} onOpenChange={(e) => { setOpen(e.open) }} placement={'center'}>
<DialogBackdrop backdropFilter='blur(1px)' />
Expand All @@ -159,13 +152,12 @@ function AddNewStrategyButton() {
<For each={defaultStrategies}>
{(strategy, index) => (
<Stack key={index} my={2}>
{isLoading && <Skeleton height={12} />}
{!isLoading && <CheckboxCard
checked={asset.strategies.some((str) => str.address === strategy.address)}
<CheckboxCard
checked={strategyExists(strategy) || selectedAsset.strategies.some((str) => str.address === strategy.address)}
onCheckedChange={(e) => handleSelectStrategy(!!e.checked, strategy)}
label={strategy.name}
/>}
{asset.strategies.some((str) => str.address === strategy.address) &&
/>
{selectedAsset.strategies.some((str) => str.address === strategy.address) &&
<Grid templateColumns={['1fr', null, 'repeat(12, 2fr)']} alignItems={'center'} >
<GridItem colSpan={2} colEnd={12}>
<Text fontSize={'xs'}>Initial deposit:</Text>
Expand All @@ -187,7 +179,7 @@ function AddNewStrategyButton() {
</GridItem>
<GridItem colStart={8} colEnd={13}>
<InputGroup
endElement={`${asset.symbol}`}
endElement={`${selectedAsset.symbol}`}
>
<Input onChange={handleAmountInput} value={amountInput.amount} />
</InputGroup>
Expand All @@ -204,7 +196,7 @@ function AddNewStrategyButton() {
Close
</Button>
<IconButton
disabled={asset.strategies.length === 0}
disabled={selectedAsset.strategies.length === 0}
aria-label='add_strategy'
colorScheme='green'
onClick={addAsset}
Expand Down
89 changes: 39 additions & 50 deletions apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import { randomBytes } from "crypto";

import { useAppDispatch, useAppSelector } from "@/store/lib/storeHooks"
import { pushVault } from '@/store/lib/features/walletStore'
import { Asset, NewVaultState } from "@/store/lib/types";
import { Asset, NewVaultState, VaultData } from "@/store/lib/types";

import { useFactoryCallback, FactoryMethod } from '@/hooks/useFactory'
import { ModalContext, TransactionStatusModalStatus } from "@/contexts";

import { AccordionItems, FormControlInterface, VaultPreview } from "./VaultPreview";
import { DialogBody, DialogCloseTrigger, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog";
import { Button } from "@chakra-ui/react"
import { resetNewVault } from "@/store/lib/features/vaultStore";
import { useVault } from "@/hooks/useVault";

interface Status {
isSuccess: boolean,
Expand All @@ -40,23 +42,15 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
const { activeChain, address } = sorobanContext;
const factory = useFactoryCallback();
const newVault: NewVaultState = useAppSelector(state => state.newVault);
//const strategies: Strategy[] = newVault.strategies;
const indexName = useAppSelector(state => state.newVault.name)
const indexSymbol = useAppSelector(state => state.newVault.symbol)
const indexShare = useAppSelector(state => state.newVault.vaultShare)
const managerString = useAppSelector(state => state.newVault.manager)
const emergencyManagerString = useAppSelector(state => state.newVault.emergencyManager)
const feeReceiverString = useAppSelector(state => state.newVault.feeReceiver)
const { transactionStatusModal: txModal } = useContext(ModalContext);
const { transactionStatusModal: txModal, deployVaultModal: deployModal } = useContext(ModalContext);
const dispatch = useAppDispatch();
const [assets, setAssets] = useState<Asset[]>([]);
const [status, setStatus] = useState<Status>({
isSuccess: false,
hasError: false,
network: undefined,
message: undefined,
txHash: undefined
});
const { getIdleFunds, getInvestedFunds, getTVL, getUserBalance } = useVault()

const [deployDisabled, setDeployDisabled] = useState(true);

Expand All @@ -73,35 +67,6 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
}
}, [managerString, emergencyManagerString, feeReceiverString])




/* useEffect(() => {
const newChartData: ChartData[] = strategies.map((strategy: Strategy, index: number) => {
return {
id: index,
label: strategy.name,
address: strategy.address,
value: strategy.share,
}
});
const total = newChartData.reduce((acc: number, curr: ChartData) => acc + curr.value, 0)
if (total == 100) {
setChartData(newChartData);
return;
} else {
newChartData.push({
id: newChartData.length,
label: 'Unassigned',
value: 100 - newChartData.reduce((acc: number, curr: ChartData) => acc + curr.value, 0),
address: undefined,
color: '#e0e0e0'
})
setChartData(newChartData);
return;
}
}, [strategies]); */

const autoCloseModal = async () => {
await new Promise(resolve => setTimeout(resolve, 30000))
txModal.resetModal();
Expand All @@ -113,7 +78,6 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
autoCloseModal();
}
}, [txModal.status])
const activeStep: number = 0;
const [buttonText, setButtonText] = useState<string>('')
const [accordionValue, setAccordionValue] = useState<AccordionItems[]>([AccordionItems.STRATEGY_DETAILS])
const [formControl, setFormControl] = useState<FormControlInterface>({
Expand Down Expand Up @@ -144,6 +108,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
}

}, [managerString, emergencyManagerString, feeReceiverString, indexShare])

const deployDefindex = async () => {
if (managerString === '' || emergencyManagerString === '') {
console.log('please fill manager config')
Expand All @@ -155,6 +120,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
setAccordionValue([AccordionItems.FEES_CONFIGS])
return
}
deployModal.setIsOpen(false)
txModal.initModal();

const vaultName = nativeToScVal(indexName, { type: "string" })
Expand Down Expand Up @@ -207,9 +173,16 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
]);
});
const assetParamsScValVec = xdr.ScVal.scvVec(assetParamsScVal);
const amountsScVal = newVault.amounts.map((amount) => {
return nativeToScVal((amount * Math.pow(10, 7)), { type: "i128" });
const amountsScVal = newVault.assets.map((asset, index) => {
const parsedAmount = newVault.amounts[index] || 0;
const truncatedAmount = Math.floor(parsedAmount * 1e7) / 1e7;
const convertedAmount = Number(truncatedAmount) * Math.pow(10, 7)
if (newVault.amounts.length === 0) return nativeToScVal(0, { type: "i128" });
return nativeToScVal(convertedAmount, { type: "i128" });
});
/* const amountsScVal = newVault.amounts.map((amount) => {
return nativeToScVal((amount * Math.pow(10, 7)), { type: "i128" });
}); */
const amountsScValVec = xdr.ScVal.scvVec(amountsScVal);
/* fn create_defindex_vault(
emergency_manager: address,
Expand All @@ -222,7 +195,9 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
salt: bytesn<32>) -> result<address,FactoryError>
*/
let result: any;
if (amountsScVal.length === 0) {


if (newVault.amounts.length === 0) {
const createDefindexParams: xdr.ScVal[] = [
emergencyManager.toScVal(),
feeReceiver.toScVal(),
Expand All @@ -242,6 +217,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
}
catch (e: any) {
console.error(e)
dispatch(resetNewVault());
txModal.handleError(e.toString());
return
}
Expand Down Expand Up @@ -269,18 +245,33 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
}
catch (e: any) {
console.error(e)
dispatch(resetNewVault());
txModal.handleError(e.toString());
return
}
}
const parsedResult: string = scValToNative(result.returnValue);
if (parsedResult.length !== 56) throw new Error('Invalid result')
const tempVault: any = {
const idleFunds = newVault.assets.map((asset, index) => {
return {
address: asset.address,
amount: newVault.amounts[index] || 0
}
})
const tempVault: VaultData = {
...newVault,
address: parsedResult
address: parsedResult,
emergencyManager: emergencyManagerString,
feeReceiver: feeReceiverString,
manager: managerString,
TVL: 0,
totalSupply: 0,
idleFunds: idleFunds,
investedFunds: [{ address: '', amount: 0 }],
}
txModal.handleSuccess(result.txHash);
await txModal.handleSuccess(result.txHash);
dispatch(pushVault(tempVault));
dispatch(resetNewVault());
return result;
}

Expand All @@ -305,14 +296,12 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo
</DialogBody>

<DialogFooter>
{(activeStep == 0 && !status.hasError) && (
<Button
aria-label='add_strategy'
colorScheme='green'
onClick={deployDefindex}>
{buttonText}
</Button>
)}
</Button>
</DialogFooter>
</>

Expand Down
Loading
Loading