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

feat: automatically expand the relevant address in the tx summary #993

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
23684df
feat: add preExpanded state for transaction. (rebased)
panteleymonchuk Jan 19, 2024
1820df4
feat: exp unlock condition
panteleymonchuk Jan 23, 2024
3aaa9e8
feat: moved to func.
panteleymonchuk Jan 23, 2024
6742b36
feat: improved func.
panteleymonchuk Jan 23, 2024
3cbf3fb
Merge remote-tracking branch 'origin/dev' into feat/issues-982-Improv…
panteleymonchuk Jan 23, 2024
6628ac1
feat: clean code.
panteleymonchuk Jan 23, 2024
6115958
chore: format fix.
panteleymonchuk Jan 23, 2024
61989ba
chore: format fix.
panteleymonchuk Jan 23, 2024
7a5c838
chore: open output by defaut (was reverted when I clean code).
panteleymonchuk Jan 23, 2024
049cffb
Merge remote-tracking branch 'origin/dev' into feat/issues-982-Improv…
panteleymonchuk Jan 23, 2024
3a1898f
chore: request details if has unlock condition expiration
panteleymonchuk Jan 23, 2024
bc0cb2a
Merge remote-tracking branch 'origin/dev' into feat/issues-982-Improv…
panteleymonchuk Jan 26, 2024
a2734ed
chore: manage expand/collapse for output through TransactionPage.
panteleymonchuk Jan 26, 2024
11470da
chore: detect unlock condition in input.
panteleymonchuk Jan 26, 2024
35e5f08
chore: update unlock condition state based on props.
panteleymonchuk Jan 28, 2024
0864291
chore: add conditions if expiration exists.
panteleymonchuk Jan 28, 2024
7a83772
chore: update by settimeout to make sure that update rerender component.
panteleymonchuk Jan 28, 2024
f0ef39a
chore: complete all conditions.
panteleymonchuk Jan 28, 2024
ed0d13b
chore: request timestamp for transactionSpentId.
panteleymonchuk Jan 28, 2024
ecf421d
fix: eslint + prettier.
panteleymonchuk Jan 28, 2024
4dc9464
chore: refactoring to simplify if/else conditions.
panteleymonchuk Jan 29, 2024
12a93e8
chore: add same conditions for outputs.
panteleymonchuk Jan 29, 2024
c436792
fix: eslint.
panteleymonchuk Jan 29, 2024
36a0e53
fix: ts
panteleymonchuk Jan 29, 2024
3e4f801
chore: remove comments from UnlockCondition component.
panteleymonchuk Jan 29, 2024
eb9647e
fix: eslint.
panteleymonchuk Jan 29, 2024
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
12 changes: 9 additions & 3 deletions client/src/app/components/stardust/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@ interface InputProps {
/**
* The inputs.
*/
readonly input: IInput;
readonly input: IInput & { unlockConditionOpenedIndexes?: number[] };
/**
* The network in context.
*/
readonly network: string;
/**
* Default expanded state.
*/
readonly isPreExpanded?: boolean;
}

/**
* Component which will display an Input on stardust.
*/
const Input: React.FC<InputProps> = ({ input, network }) => {
const Input: React.FC<InputProps> = ({ input, network, isPreExpanded }) => {
const history = useHistory();
const { tokenInfo } = useContext(NetworkContext);
const [isExpanded, setIsExpanded] = useState(false);
const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the basic output doesnt look expanded by default, we should expand all the outputs to see at first glance what is the address that spent it for inputs, and where they got sent for the output part
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should look like this
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, it was expanded. I'll doublecheck it.

const [isFormattedBalance, setIsFormattedBalance] = useState(true);

const fallbackInputView = (
Expand Down Expand Up @@ -84,11 +88,13 @@ const Input: React.FC<InputProps> = ({ input, network }) => {

return input.output ? (
<Output
isPreExpanded={isPreExpanded}
outputId={outputId}
output={input.output.output}
amount={Number(input.output.output.amount)}
network={network}
showCopyAmount={true}
unlockConditionOpenedIndexes={input.unlockConditionOpenedIndexes}
/>
) : (
fallbackInputView
Expand Down
15 changes: 12 additions & 3 deletions client/src/app/components/stardust/Output.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,18 @@ class Output extends Component<OutputProps, OutputState> {
{/* all output types except Treasury have common output conditions */}
{output.type !== OutputType.Treasury && (
<React.Fragment>
{(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => (
<UnlockCondition key={idx} unlockCondition={unlockCondition} isPreExpanded={isPreExpanded} />
))}
{(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => {
const isExpandedByCondition =
this.props.unlockConditionOpenedIndexes && this.props.unlockConditionOpenedIndexes.includes(idx);

return (
<UnlockCondition
key={outputId + idx}
unlockCondition={unlockCondition}
isPreExpanded={isExpandedByCondition ?? isPreExpanded}
/>
);
})}
{(output as CommonOutput).features?.map((feature, idx) => (
<Feature
key={idx}
Expand Down
5 changes: 5 additions & 0 deletions client/src/app/components/stardust/OutputProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ export interface OutputProps {
* Disable links if block is conflicting.
*/
isLinksDisabled?: boolean;

/**
* Indexes for unlock conditions that need to be opened.
*/
unlockConditionOpenedIndexes?: number[];
}
186 changes: 85 additions & 101 deletions client/src/app/components/stardust/UnlockCondition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
UnlockConditionType,
} from "@iota/sdk-wasm/web";
import classNames from "classnames";
import React, { ReactNode } from "react";
import React, { useContext, useState, useEffect } from "react";
import DropdownIcon from "~assets/dropdown-arrow.svg?react";
import Address from "./address/Address";
import { UnlockConditionProps } from "./UnlockConditionProps";
Expand All @@ -19,114 +19,98 @@ import { DateHelper } from "~helpers/dateHelper";
import { NameHelper } from "~helpers/stardust/nameHelper";
import { formatAmount } from "~helpers/stardust/valueFormatHelper";
import NetworkContext from "../../context/NetworkContext";
import AsyncComponent from "../AsyncComponent";

/**
* Component which will display an unlock condition.
*/
class UnlockCondition extends AsyncComponent<UnlockConditionProps, UnlockConditionState> {
/**
* The component context type.
*/
public static contextType = NetworkContext;
function UnlockCondition(props: UnlockConditionProps) {
const [state, setState] = useState<UnlockConditionState>({
isFormattedBalance: true,
isExpanded: props.isPreExpanded ?? false,
});
const context = useContext(NetworkContext);
const { isFormattedBalance, isExpanded } = state;
const { unlockCondition } = props;
const tokenInfo: INodeInfoBaseToken = context.tokenInfo;

/**
* The component context.
*/
public declare context: React.ContextType<typeof NetworkContext>;
useEffect(() => {
if (props.isPreExpanded !== undefined && props.isPreExpanded !== null) {
setState((prevState) => ({
...prevState,
isExpanded: props.isPreExpanded ?? false,
}));
}
}, [props.isPreExpanded]);

constructor(props: UnlockConditionProps) {
super(props);

this.state = {
isFormattedBalance: true,
isExpanded: this.props.isPreExpanded ?? false,
};
}

/**
* The component mounted.
*/
public async componentDidMount(): Promise<void> {
super.componentDidMount();
}

/**
* Render the component.
* @returns The node to render.
*/
public render(): ReactNode {
const { isFormattedBalance, isExpanded } = this.state;
const { unlockCondition } = this.props;
const tokenInfo: INodeInfoBaseToken = this.context.tokenInfo;

return (
<div className="unlock-condition">
<div className="card--content__input card--value row middle" onClick={() => this.setState({ isExpanded: !isExpanded })}>
<div className={classNames("margin-r-t", "card--content--dropdown", { opened: isExpanded })}>
<DropdownIcon />
</div>
<div className="card--label">{NameHelper.getUnlockConditionTypeName(unlockCondition.type)}</div>
return (
<div className="unlock-condition">
<div
className="card--content__input card--value row middle"
onClick={() => setState((p) => ({ ...p, isExpanded: !isExpanded }))}
>
<div className={classNames("margin-r-t", "card--content--dropdown", { opened: isExpanded })}>
<DropdownIcon />
</div>
{isExpanded && (
<div className="padding-l-t left-border">
{unlockCondition.type === UnlockConditionType.Address && (
<Address address={(unlockCondition as AddressUnlockCondition).address} />
)}
{unlockCondition.type === UnlockConditionType.StorageDepositReturn && (
<React.Fragment>
<div className="card--label">Return address</div>
<Address address={(unlockCondition as StorageDepositReturnUnlockCondition).returnAddress} />
<div className="card--label">Amount:</div>
<div className="card--value row">
<span
className="pointer margin-r-t"
onClick={() => this.setState({ isFormattedBalance: !isFormattedBalance })}
>
{formatAmount(
Number((unlockCondition as StorageDepositReturnUnlockCondition).amount),
tokenInfo,
!isFormattedBalance,
)}
</span>
</div>
</React.Fragment>
)}
{unlockCondition.type === UnlockConditionType.Timelock && (unlockCondition as TimelockUnlockCondition).unixTime && (
<React.Fragment>
<div className="card--label">Unix time</div>
<div className="card--value row">
{DateHelper.formatShort((unlockCondition as TimelockUnlockCondition).unixTime * 1000)}
</div>
</React.Fragment>
)}
{unlockCondition.type === UnlockConditionType.Expiration && (
<React.Fragment>
<Address address={(unlockCondition as ExpirationUnlockCondition).returnAddress} />
{(unlockCondition as ExpirationUnlockCondition).unixTime && (
<React.Fragment>
<div className="card--label">Unix time</div>
<div className="card--value row">
{DateHelper.formatShort((unlockCondition as ExpirationUnlockCondition).unixTime * 1000)}
</div>
</React.Fragment>
)}
</React.Fragment>
)}
{unlockCondition.type === UnlockConditionType.GovernorAddress && (
<Address address={(unlockCondition as GovernorAddressUnlockCondition).address} />
)}
{unlockCondition.type === UnlockConditionType.ImmutableAliasAddress && (
<Address address={(unlockCondition as ImmutableAliasAddressUnlockCondition).address} />
)}
{unlockCondition.type === UnlockConditionType.StateControllerAddress && (
<Address address={(unlockCondition as StateControllerAddressUnlockCondition).address} />
)}
</div>
)}
<div className="card--label">{NameHelper.getUnlockConditionTypeName(unlockCondition.type)}</div>
</div>
);
}
{isExpanded && (
<div className="padding-l-t left-border">
{unlockCondition.type === UnlockConditionType.Address && (
<Address address={(unlockCondition as AddressUnlockCondition).address} />
)}
{unlockCondition.type === UnlockConditionType.StorageDepositReturn && (
<React.Fragment>
<div className="card--label">Return address</div>
<Address address={(unlockCondition as StorageDepositReturnUnlockCondition).returnAddress} />
<div className="card--label">Amount:</div>
<div className="card--value row">
<span
className="pointer margin-r-t"
onClick={() => setState((p) => ({ ...p, isFormattedBalance: !isFormattedBalance }))}
>
{formatAmount(
Number((unlockCondition as StorageDepositReturnUnlockCondition).amount),
tokenInfo,
!isFormattedBalance,
)}
</span>
</div>
</React.Fragment>
)}
{unlockCondition.type === UnlockConditionType.Timelock && (unlockCondition as TimelockUnlockCondition).unixTime && (
<React.Fragment>
<div className="card--label">Unix time</div>
<div className="card--value row">
{DateHelper.formatShort((unlockCondition as TimelockUnlockCondition).unixTime * 1000)}
</div>
</React.Fragment>
)}
{unlockCondition.type === UnlockConditionType.Expiration && (
<React.Fragment>
<Address address={(unlockCondition as ExpirationUnlockCondition).returnAddress} />
{(unlockCondition as ExpirationUnlockCondition).unixTime && (
<React.Fragment>
<div className="card--label">Unix time</div>
<div className="card--value row">
{DateHelper.formatShort((unlockCondition as ExpirationUnlockCondition).unixTime * 1000)}
</div>
</React.Fragment>
)}
</React.Fragment>
)}
{unlockCondition.type === UnlockConditionType.GovernorAddress && (
<Address address={(unlockCondition as GovernorAddressUnlockCondition).address} />
)}
{unlockCondition.type === UnlockConditionType.ImmutableAliasAddress && (
<Address address={(unlockCondition as ImmutableAliasAddressUnlockCondition).address} />
)}
{unlockCondition.type === UnlockConditionType.StateControllerAddress && (
<Address address={(unlockCondition as StateControllerAddressUnlockCondition).address} />
)}
</div>
)}
</div>
);
}

export default UnlockCondition;
8 changes: 6 additions & 2 deletions client/src/app/components/stardust/Unlocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import { NameHelper } from "~helpers/stardust/nameHelper";

interface IUnlocksProps {
readonly unlocks: Unlock[];
/**
* Default expanded state.
*/
readonly isPreExpanded?: boolean;
}

const Unlocks: React.FC<IUnlocksProps> = ({ unlocks }) => {
const [isExpanded, setIsExpanded] = useState(false);
const Unlocks: React.FC<IUnlocksProps> = ({ unlocks, isPreExpanded }) => {
const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? false);

const displayUnlocksTypeAndIndex = (type: number, index: number) => (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class TransactionPayload extends AsyncComponent<TransactionPayloadProps, Transac
*/
public render(): ReactNode {
const { network, inputs, unlocks, outputs, header, isLinksDisabled } = this.props;

return (
<div className="transaction-payload">
{header && (
Expand All @@ -69,7 +68,7 @@ class TransactionPayload extends AsyncComponent<TransactionPayloadProps, Transac
</div>
<div className="transaction-payload_outputs card--content">
{inputs.map((input, idx) => (
<Input key={idx} network={network} input={input} />
<Input key={idx} network={network} input={input} isPreExpanded={true} />
))}
<Unlocks unlocks={unlocks} />
</div>
Expand All @@ -90,7 +89,9 @@ class TransactionPayload extends AsyncComponent<TransactionPayloadProps, Transac
amount={output.amount}
network={network}
showCopyAmount={true}
isPreExpanded={true}
isLinksDisabled={isLinksDisabled}
unlockConditionOpenedIndexes={output.unlockConditionOpenedIndexes}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface TransactionPayloadProps {
/**
* The inputs.
*/
inputs: IInput[];
inputs: (IInput & { unlockConditionOpenedIndexes?: number[] })[];

/**
* The unlocks of the transaction.
Expand All @@ -21,7 +21,7 @@ export interface TransactionPayloadProps {
/**
* The outputs.
*/
outputs: IOutput[];
outputs: (IOutput & { unlockConditionOpenedIndexes?: number[] })[];

/**
* The header title of this section.
Expand Down
Loading
Loading