Skip to content

Commit

Permalink
feat(website): queue transactions improve ux (#606)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xDmitry authored Nov 10, 2023
1 parent fc4868e commit c319185
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 64 deletions.
8 changes: 6 additions & 2 deletions packages/website/src/app/deploy/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
'use client';

import dynamic from 'next/dynamic';
import { ReactNode } from 'react';
import { Box, Flex, useBreakpointValue } from '@chakra-ui/react';
import { usePathname } from 'next/navigation';
import { links } from '@/constants/links';
import { NavLink } from '@/components/NavLink';
import WithSafe from '@/features/Deploy/WithSafe';

const NoSSRWithSafe = dynamic(() => import('@/features/Deploy/WithSafe'), {
ssr: false,
});

export default function DeployLayout({ children }: { children: ReactNode }) {
const pathname = usePathname();
Expand Down Expand Up @@ -47,7 +51,7 @@ export default function DeployLayout({ children }: { children: ReactNode }) {
</NavLink>
</Flex>
</Box>
<WithSafe>{children}</WithSafe>
<NoSSRWithSafe>{children}</NoSSRWithSafe>
</Flex>
);
}
30 changes: 7 additions & 23 deletions packages/website/src/components/EditableAutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export function EditableAutocompleteInput(props: {
const editableInputRef = useRef();

function tabToNext() {
console.log('tabdata trigger tabtonext');
const tabElements = Array.from(
document
// Get all elements that can be focusable
Expand Down Expand Up @@ -144,10 +143,6 @@ export function EditableAutocompleteInput(props: {
(e: any) => e === editableInputRef.current
);

//console.log('tabdata current index', currentIndex);
//console.log('tabdata', editableInputRef)
//console.log('tabdata', tabElements);

const nextIndex = (currentIndex + 1) % tabElements.length;
tabElements[nextIndex].focus();
}
Expand Down Expand Up @@ -201,7 +196,6 @@ export function EditableAutocompleteInput(props: {
onEdit={() => setIsEditing(true)}
onBlur={() => {
finishEdit();
tabToNext();
}}
onKeyDown={handleKey}
onChange={(value) => {
Expand Down Expand Up @@ -243,15 +237,14 @@ export function EditableAutocompleteInput(props: {
key={index}
item={item}
filterInput={filterInput}
selected={item.label === pendingItem}
isVisible={isEditing && filteredItems.length > 0}
onMouseOver={() => setPendingItem(item.label)}
onMouseDown={(evt: any) => {
evt.preventDefault();
}}
onClick={() => {
console.log('tabdata click');
setPendingItem(item.label);
setFilterInput(item.label);
tabToNext();
console.log('tabdata end');
}}
internalRef={
(item.label === pendingItem
Expand All @@ -271,10 +264,9 @@ export function EditableAutocompleteInput(props: {
function AutocompleteOption(props: {
item: { label: string; secondary: string };
filterInput: string;
selected?: boolean;
onMouseOver: () => void;
onMouseDown: (evt: any) => void;
onClick: () => void;
isVisible: boolean;
// eslint-disable-next-line no-undef
internalRef: React.MutableRefObject<HTMLDivElement> | undefined;
}) {
Expand All @@ -287,21 +279,13 @@ function AutocompleteOption(props: {
<Box
ref={props.internalRef}
onMouseOver={props.onMouseOver}
onClick={(evt) => {
evt.preventDefault();
props.onClick();
}}
onMouseDown={(evt: any) => props.onMouseDown(evt)}
onClick={props.onClick}
background="black"
px="2"
pb="1"
>
<HStack
onClick={(evt) => {
evt.preventDefault();
props.onClick();
}}
gap={0}
>
<HStack gap={0}>
{matched.map((p, i) => [
<Text key={i}>{p}</Text>,
i < matched.length - 1 ? <Text as="b">{props.filterInput}</Text> : [],
Expand Down
117 changes: 79 additions & 38 deletions packages/website/src/features/Deploy/QueueTransactionsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { AddIcon, MinusIcon } from '@chakra-ui/icons';
import { AddIcon } from '@chakra-ui/icons';
import {
Alert,
AlertDescription,
Expand All @@ -18,9 +18,11 @@ import {
Tooltip,
useToast,
Text,
IconButton,
Flex,
} from '@chakra-ui/react';
import _ from 'lodash';
import { useState } from 'react';
import { CloseIcon } from '@chakra-ui/icons';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import {
Abi,
Expand All @@ -42,6 +44,11 @@ import { makeMultisend } from '@/helpers/multisend';
import { DisplayedTransaction } from './DisplayedTransaction';
import NoncePicker from './NoncePicker';

type IdentifiableTxn = {
txn: Omit<TransactionRequestBase, 'from'>;
id: string;
};

export default function QueueTransactionsPage() {
return <QueueTransactions />;
}
Expand All @@ -51,9 +58,11 @@ function QueueTransactions() {
const router = useRouter();

const [target, setTarget] = useState('');
const [queuedTxns, setQueuedTxns] = useState<
Omit<TransactionRequestBase, 'from'>[]
>([null as any]);

const [lastQueuedTxnsId, setLastQueuedTxnsId] = useState(0);
const [queuedIdentifiableTxns, setQueuedIdentifiableTxns] = useState<
IdentifiableTxn[]
>([{ txn: null as any, id: String(lastQueuedTxnsId) }]);

const [pickedNonce, setPickedNonce] = useState<number | null>(null);

Expand All @@ -63,6 +72,8 @@ function QueueTransactions() {
`${currentSafe?.chainId}-${settings.preset}`
);

const queuedTxns = queuedIdentifiableTxns.map((item) => item.txn);

const multisendTxn =
queuedTxns.indexOf(null as any) === -1
? makeMultisend(
Expand Down Expand Up @@ -127,10 +138,29 @@ function QueueTransactions() {
i: number,
txn: Omit<TransactionRequestBase, 'from'>
) {
queuedTxns[i] = txn;
setQueuedTxns(_.clone(queuedTxns));
setQueuedIdentifiableTxns((prev) => {
const result = [...prev];
result[i].txn = txn;
return result;
});
}

const removeQueuedTxn = (i: number) => {
setQueuedIdentifiableTxns((prev) => {
const result = [...prev];
result.splice(i, 1);
return result;
});
};

const addQueuedTxn = () => {
setQueuedIdentifiableTxns((prev) => [
...prev,
{ txn: {}, id: String(lastQueuedTxnsId + 1) },
]);
setLastQueuedTxnsId((prev) => prev + 1);
};

const txnHasError = !!txnInfo.txnResults.filter((r) => r?.error).length;

console.log('TXN HAS ERROR', txnHasError);
Expand Down Expand Up @@ -158,6 +188,17 @@ function QueueTransactions() {
const disableExecute =
!multisendTxn || txnHasError || !!stager.execConditionFailed;

console.log('xxx cannonInfo: ', cannonInfo);

useEffect(() => {
if (!cannonInfo.contracts) {
setQueuedIdentifiableTxns([
{ txn: null as any, id: String(lastQueuedTxnsId + 1) },
]);
setLastQueuedTxnsId((prev) => prev + 1);
}
}, [cannonInfo.contracts]);

return (
<Container maxWidth="container.md" py={8}>
<Box mb={6}>
Expand All @@ -182,7 +223,7 @@ function QueueTransactions() {
connected wallet.
</FormHelperText>
</FormControl>
{cannonInfo.pkgUrl && !cannonInfo.contracts && (
{!isAddress(target) && cannonInfo.pkgUrl && !cannonInfo.contracts && (
<Alert bg="gray.800" status="info">
<AlertIcon />
<Box>
Expand All @@ -193,18 +234,32 @@ function QueueTransactions() {
</Box>
</Alert>
)}
{cannonInfo.contracts && (
{!isAddress(target) && cannonInfo.contracts && (
<FormControl mb="8">
<FormLabel>Transactions</FormLabel>
{queuedTxns.map((_, i) => (
<Box key={i} mb={3}>
<DisplayedTransaction
editable
contracts={cannonInfo.contracts as any}
onTxn={(txn) => updateQueuedTxn(i, txn as any)}
/>
{queuedIdentifiableTxns.map((queuedIdentifiableTxn, i) => (
<Flex key={queuedIdentifiableTxn.id} mb={3} direction="column">
<Flex>
<DisplayedTransaction
editable
contracts={cannonInfo.contracts as any}
txn={queuedIdentifiableTxn.txn}
onTxn={(txn) => updateQueuedTxn(i, txn as any)}
/>
{queuedIdentifiableTxns.length > 1 && (
<Box ml="3">
<IconButton
colorScheme="blackAlpha"
background="transparent"
icon={<CloseIcon opacity="0.5" />}
aria-label={'Remove provider'}
onClick={() => removeQueuedTxn(i)}
/>
</Box>
)}
</Flex>
{txnInfo.txnResults &&
txnInfo.txnResults.length === queuedTxns.length &&
txnInfo.txnResults.length === queuedIdentifiableTxns.length &&
txnInfo.txnResults[i] &&
txnInfo.txnResults[i]?.error && (
<Alert bg="gray.800" status="error" mt="6">
Expand All @@ -215,7 +270,7 @@ function QueueTransactions() {
: txnInfo.txnResults[i]?.error}
</Alert>
)}
</Box>
</Flex>
))}
<HStack my="3">
<Button
Expand All @@ -226,28 +281,10 @@ function QueueTransactions() {
borderColor="green.400"
_hover={{ bg: 'green.900' }}
leftIcon={<AddIcon />}
onClick={() => setQueuedTxns(_.clone(queuedTxns.concat([{}])))}
onClick={() => addQueuedTxn()}
>
Add Transaction
</Button>
{queuedTxns.length > 1 && (
<Button
variant="outline"
size="xs"
colorScheme="red"
color="red.400"
borderColor="red.400"
_hover={{ bg: 'red.900' }}
leftIcon={<MinusIcon />}
onClick={() =>
setQueuedTxns(
_.clone(queuedTxns.slice(0, queuedTxns.length - 1))
)
}
>
Remove Transaction
</Button>
)}
</HStack>
</FormControl>
)}
Expand All @@ -256,6 +293,8 @@ function QueueTransactions() {
<FormLabel>Value</FormLabel>
<Input
type="text"
borderColor="whiteAlpha.400"
background="black"
onChange={(event: any) =>
updateQueuedTxn(0, {
...queuedTxns[0],
Expand All @@ -273,6 +312,8 @@ function QueueTransactions() {
<FormLabel>Transaction Data</FormLabel>
<Input
type="text"
borderColor="whiteAlpha.400"
background="black"
placeholder="0x"
onChange={(event: any) =>
updateQueuedTxn(0, {
Expand Down
1 change: 1 addition & 0 deletions packages/website/src/features/Deploy/SafeAddressInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export function SafeAddressInput() {
<>
<FormControl>
<CreatableSelect
instanceId={'safe-address-select'}
chakraStyles={chakraStyles}
isClearable
value={currentSafe ? _safeToOption(currentSafe) : null}
Expand Down
6 changes: 5 additions & 1 deletion packages/website/src/hooks/cannon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,12 +415,16 @@ export function useCannonPackageContracts(packageRef: string, variant = '') {

if (outputs) {
setContracts(getContractsRecursive(outputs, null as any));
} else {
setContracts(null);
}
} else {
setContracts(null);
}
};

void getContracts();
}, [pkg.pkg]);
}, [pkg.pkg, packageRef]);

return { contracts, ...pkg };
}

0 comments on commit c319185

Please sign in to comment.