From 23684df42306e2fc68854d1e46b387785ec3cd41 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 19 Jan 2024 19:12:09 +0200 Subject: [PATCH 01/23] feat: add preExpanded state for transaction. (rebased) Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 10 ++++++++-- client/src/app/components/stardust/Unlocks.tsx | 8 ++++++-- .../stardust/block/payload/TransactionPayload.tsx | 5 +++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 8fa0dcfb2..7ad7d79e6 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -20,15 +20,20 @@ interface InputProps { * 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 = ({ input, network }) => { +const Input: React.FC = ({ input, network, isPreExpanded }) => { + const history = useHistory(); const { tokenInfo } = useContext(NetworkContext); - const [isExpanded, setIsExpanded] = useState(false); + const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? false); const [isFormattedBalance, setIsFormattedBalance] = useState(true); const fallbackInputView = ( @@ -84,6 +89,7 @@ const Input: React.FC = ({ input, network }) => { return input.output ? ( = ({ unlocks }) => { - const [isExpanded, setIsExpanded] = useState(false); +const Unlocks: React.FC = ({ unlocks, isPreExpanded }) => { + const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? false); const displayUnlocksTypeAndIndex = (type: number, index: number) => (
diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index a7a023fe4..4bd544072 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -69,9 +69,9 @@ class TransactionPayload extends AsyncComponent
{inputs.map((input, idx) => ( - + ))} - +
@@ -84,6 +84,7 @@ class TransactionPayload extends AsyncComponent {outputs.map((output, idx) => ( Date: Tue, 23 Jan 2024 16:16:58 +0200 Subject: [PATCH 02/23] feat: exp unlock condition Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 2 +- client/src/app/components/stardust/Output.tsx | 34 ++++++++++++++++--- .../block/payload/TransactionPayload.tsx | 5 ++- .../app/routes/stardust/TransactionPage.tsx | 20 +++++++++++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 7ad7d79e6..7d0edbe41 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -33,7 +33,7 @@ const Input: React.FC = ({ input, network, isPreExpanded }) => { const history = useHistory(); const { tokenInfo } = useContext(NetworkContext); - const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? false); + const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? true); const [isFormattedBalance, setIsFormattedBalance] = useState(true); const fallbackInputView = ( diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 65af6ff5b..c85c4d682 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -61,7 +61,7 @@ class Output extends Component { super(props); this.state = { - isExpanded: this.props.isPreExpanded ?? false, + isExpanded: this.props.isPreExpanded ?? true, isFormattedBalance: true, }; } @@ -72,6 +72,10 @@ class Output extends Component { */ public render(): ReactNode { const { outputId, output, amount, showCopyAmount, network, isPreExpanded, displayFullOutputId, isLinksDisabled } = this.props; + + // console.log('--- output', output); + // (output as CommonOutput) + const { isExpanded, isFormattedBalance } = this.state; const tokenInfo: INodeInfoBaseToken = this.context.tokenInfo; @@ -85,6 +89,12 @@ class Output extends Component { const isSpecialCondition = this.hasSpecialCondition(); const isParticipationOutput = TransactionsHelper.isParticipationEventOutput(output); + const hasStorageDepositReturnUnlockCondition = (output as CommonOutput).unlockConditions.find( + (condition) => condition.type === UnlockConditionType.StorageDepositReturn + ); + + console.log('--- hasStorageDepositReturnUnlockCondition', hasStorageDepositReturnUnlockCondition); + const specialUnlockCondition = output.type !== OutputType.Treasury && isSpecialCondition && @@ -223,9 +233,25 @@ class Output extends Component { {/* all output types except Treasury have common output conditions */} {output.type !== OutputType.Treasury && ( - {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => ( - - ))} + {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { + let isExpandedByCondition = false; + + if (!hasStorageDepositReturnUnlockCondition && unlockCondition.type === UnlockConditionType.Address) { + isExpandedByCondition = true; + } + + if (unlockCondition.type === UnlockConditionType.Address && hasStorageDepositReturnUnlockCondition && + // @ts-ignore + hasStorageDepositReturnUnlockCondition?.returnAddress?.pubKeyHash === unlockCondition.address?.pubKeyHash + ) { + isExpandedByCondition = true; + } + // console.log('--- isExpandedByCondition', isExpandedByCondition); + + return ( + + ) + })} {(output as CommonOutput).features?.map((feature, idx) => (
{inputs.map((input, idx) => ( - + ))} - +
@@ -84,7 +84,6 @@ class TransactionPayload extends AsyncComponent {outputs.map((output, idx) => ( > = ({ const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); + console.log('--- inputs', inputs); + // @ts-ignore + inputs && inputs[0]?.output?.output?.unlockConditions.push({ + "type": 1, + "amount": 85000000, + "returnAddress": { + "type": 0, + "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d", + + } + }) + // @ts-ignore + inputs && inputs[0]?.output?.output?.unlockConditions.push({ + "type": 0, + "address": { + "type": 0, + "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d" + } + }) + useEffect(() => { if (block?.payload?.type === PayloadType.Transaction) { const transactionPayload = block.payload as ITransactionPayload; From 3aaa9e864541103c72b8a83d64b566913d4cf178 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 17:13:47 +0200 Subject: [PATCH 03/23] feat: moved to func. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 48 ++++++++++++------- .../app/routes/stardust/TransactionPage.tsx | 36 +++++++------- client/src/helpers/dateHelper.ts | 9 ++++ 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index c85c4d682..8336659b3 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -89,11 +89,8 @@ class Output extends Component { const isSpecialCondition = this.hasSpecialCondition(); const isParticipationOutput = TransactionsHelper.isParticipationEventOutput(output); - const hasStorageDepositReturnUnlockCondition = (output as CommonOutput).unlockConditions.find( - (condition) => condition.type === UnlockConditionType.StorageDepositReturn - ); - console.log('--- hasStorageDepositReturnUnlockCondition', hasStorageDepositReturnUnlockCondition); + const unlockConditionExpiredType = this.getUnlockConditionExpiredType((output as CommonOutput)?.unlockConditions); const specialUnlockCondition = output.type !== OutputType.Treasury && @@ -234,20 +231,10 @@ class Output extends Component { {output.type !== OutputType.Treasury && ( {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { - let isExpandedByCondition = false; - - if (!hasStorageDepositReturnUnlockCondition && unlockCondition.type === UnlockConditionType.Address) { - isExpandedByCondition = true; - } - - if (unlockCondition.type === UnlockConditionType.Address && hasStorageDepositReturnUnlockCondition && - // @ts-ignore - hasStorageDepositReturnUnlockCondition?.returnAddress?.pubKeyHash === unlockCondition.address?.pubKeyHash - ) { - isExpandedByCondition = true; - } - // console.log('--- isExpandedByCondition', isExpandedByCondition); - + const isExpandedByCondition = this.getExpandedStateInUnlockCondition( + unlockCondition, + unlockConditionExpiredType as ExpirationUnlockCondition + ); return ( ) @@ -284,6 +271,31 @@ class Output extends Component { ); } + private getUnlockConditionExpiredType(unlockConditions?: IUnlockCondition[]): IUnlockCondition | undefined { + if (!unlockConditions) return; + return unlockConditions.find((condition) => condition.type === UnlockConditionType.Expiration); + } + + private getExpandedStateInUnlockCondition( + unlockCondition?: IUnlockCondition, + expirationUnlockCondition?: ExpirationUnlockCondition + ): boolean { + + if (unlockCondition?.type === UnlockConditionType.Address && !expirationUnlockCondition) return true; + + const isExpirationUnlockConditionExpired = DateHelper.isExpired((expirationUnlockCondition as ExpirationUnlockCondition).unixTime * 1000); + + if (unlockCondition?.type === UnlockConditionType.Expiration && isExpirationUnlockConditionExpired) { + return true; + } + + if (unlockCondition?.type === UnlockConditionType.Address && !isExpirationUnlockConditionExpired) { + return true; + } + + return false; + } + /** * Build bech32 address. * @returns The bech32 address. diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index df80466fe..2d1b624c0 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -46,24 +46,24 @@ const TransactionPage: React.FC> = ({ const [isFormattedBalance, setIsFormattedBalance] = useState(true); console.log('--- inputs', inputs); - // @ts-ignore - inputs && inputs[0]?.output?.output?.unlockConditions.push({ - "type": 1, - "amount": 85000000, - "returnAddress": { - "type": 0, - "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d", - - } - }) - // @ts-ignore - inputs && inputs[0]?.output?.output?.unlockConditions.push({ - "type": 0, - "address": { - "type": 0, - "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d" - } - }) + // // @ts-ignore + // inputs && inputs[0]?.output?.output?.unlockConditions.push({ + // "type": 1, + // "amount": 85000000, + // "returnAddress": { + // "type": 0, + // "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d", + // + // } + // }) + // // @ts-ignore + // inputs && inputs[0]?.output?.output?.unlockConditions.push({ + // "type": 0, + // "address": { + // "type": 0, + // "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d" + // } + // }) useEffect(() => { if (block?.payload?.type === PayloadType.Transaction) { diff --git a/client/src/helpers/dateHelper.ts b/client/src/helpers/dateHelper.ts index 06388ddef..03650b604 100644 --- a/client/src/helpers/dateHelper.ts +++ b/client/src/helpers/dateHelper.ts @@ -39,6 +39,15 @@ export class DateHelper { return moment(valueInMs).format("YYYY-MM-DD HH:mm:ss"); } + /** + * Check if the date is expired. + * @param valueInMs The value to format in milliseconds. + * @returns Boolean if the date is expired. + */ + public static isExpired(valueInMs: number): boolean { + return moment(valueInMs).isBefore(moment()); + } + /** * Check the value is in ms if not scale accordingly. * @param valueInMs The value to format in milliseconds. From 6742b36eefa96fc367d655c97c9c52d3b0d369cb Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 17:40:22 +0200 Subject: [PATCH 04/23] feat: improved func. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 8336659b3..49a1f5326 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -90,8 +90,6 @@ class Output extends Component { const isParticipationOutput = TransactionsHelper.isParticipationEventOutput(output); - const unlockConditionExpiredType = this.getUnlockConditionExpiredType((output as CommonOutput)?.unlockConditions); - const specialUnlockCondition = output.type !== OutputType.Treasury && isSpecialCondition && @@ -233,7 +231,6 @@ class Output extends Component { {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { const isExpandedByCondition = this.getExpandedStateInUnlockCondition( unlockCondition, - unlockConditionExpiredType as ExpirationUnlockCondition ); return ( @@ -271,29 +268,21 @@ class Output extends Component { ); } - private getUnlockConditionExpiredType(unlockConditions?: IUnlockCondition[]): IUnlockCondition | undefined { - if (!unlockConditions) return; - return unlockConditions.find((condition) => condition.type === UnlockConditionType.Expiration); - } - - private getExpandedStateInUnlockCondition( - unlockCondition?: IUnlockCondition, - expirationUnlockCondition?: ExpirationUnlockCondition - ): boolean { - - if (unlockCondition?.type === UnlockConditionType.Address && !expirationUnlockCondition) return true; + private getExpandedStateInUnlockCondition(unlockCondition?: IUnlockCondition, unlockConditions?: IUnlockCondition[]): boolean { + if (!unlockCondition) return false; - const isExpirationUnlockConditionExpired = DateHelper.isExpired((expirationUnlockCondition as ExpirationUnlockCondition).unixTime * 1000); + const expirationUnlockCondition = unlockConditions?.find(cond => cond.type === UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const isExpirationConditionPresent = !!expirationUnlockCondition; + const isExpirationConditionExpired = isExpirationConditionPresent && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); - if (unlockCondition?.type === UnlockConditionType.Expiration && isExpirationUnlockConditionExpired) { - return true; + switch (unlockCondition.type) { + case UnlockConditionType.Address: + return !isExpirationConditionPresent || (isExpirationConditionPresent && !isExpirationConditionExpired); + case UnlockConditionType.Expiration: + return isExpirationConditionExpired; + default: + return false; } - - if (unlockCondition?.type === UnlockConditionType.Address && !isExpirationUnlockConditionExpired) { - return true; - } - - return false; } /** From 6628ac1a9e15766bff28f18789ebca5ef415e6fc Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 17:41:53 +0200 Subject: [PATCH 05/23] feat: clean code. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 7 +------ .../app/routes/stardust/TransactionPage.tsx | 20 ------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 49a1f5326..3adfbc8be 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -61,7 +61,7 @@ class Output extends Component { super(props); this.state = { - isExpanded: this.props.isPreExpanded ?? true, + isExpanded: this.props.isPreExpanded ?? false, isFormattedBalance: true, }; } @@ -72,10 +72,6 @@ class Output extends Component { */ public render(): ReactNode { const { outputId, output, amount, showCopyAmount, network, isPreExpanded, displayFullOutputId, isLinksDisabled } = this.props; - - // console.log('--- output', output); - // (output as CommonOutput) - const { isExpanded, isFormattedBalance } = this.state; const tokenInfo: INodeInfoBaseToken = this.context.tokenInfo; @@ -89,7 +85,6 @@ class Output extends Component { const isSpecialCondition = this.hasSpecialCondition(); const isParticipationOutput = TransactionsHelper.isParticipationEventOutput(output); - const specialUnlockCondition = output.type !== OutputType.Treasury && isSpecialCondition && diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 2d1b624c0..b805a26c7 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -45,26 +45,6 @@ const TransactionPage: React.FC> = ({ const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); - console.log('--- inputs', inputs); - // // @ts-ignore - // inputs && inputs[0]?.output?.output?.unlockConditions.push({ - // "type": 1, - // "amount": 85000000, - // "returnAddress": { - // "type": 0, - // "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d", - // - // } - // }) - // // @ts-ignore - // inputs && inputs[0]?.output?.output?.unlockConditions.push({ - // "type": 0, - // "address": { - // "type": 0, - // "pubKeyHash": "0xfd13a83be173b38bbb9a02415a7a8b91acf691b762edd0945e9bb865e5816c9d" - // } - // }) - useEffect(() => { if (block?.payload?.type === PayloadType.Transaction) { const transactionPayload = block.payload as ITransactionPayload; From 611595860b19b743d586e8f17b51df9d53a5a1dc Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 17:46:54 +0200 Subject: [PATCH 06/23] chore: format fix. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 3adfbc8be..90667bbd4 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -224,12 +224,14 @@ class Output extends Component { {output.type !== OutputType.Treasury && ( {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { - const isExpandedByCondition = this.getExpandedStateInUnlockCondition( - unlockCondition, - ); + const isExpandedByCondition = this.getExpandedStateInUnlockCondition(unlockCondition); return ( - - ) + + ); })} {(output as CommonOutput).features?.map((feature, idx) => ( { private getExpandedStateInUnlockCondition(unlockCondition?: IUnlockCondition, unlockConditions?: IUnlockCondition[]): boolean { if (!unlockCondition) return false; - const expirationUnlockCondition = unlockConditions?.find(cond => cond.type === UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const expirationUnlockCondition = unlockConditions?.find( + (cond) => cond.type === UnlockConditionType.Expiration, + ) as ExpirationUnlockCondition; const isExpirationConditionPresent = !!expirationUnlockCondition; - const isExpirationConditionExpired = isExpirationConditionPresent && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); + const isExpirationConditionExpired = + isExpirationConditionPresent && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); switch (unlockCondition.type) { case UnlockConditionType.Address: From 61989ba85162a9903cd4751672fd0c027569f21b Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 17:50:02 +0200 Subject: [PATCH 07/23] chore: format fix. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 7d0edbe41..2b8589127 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -30,7 +30,6 @@ interface InputProps { * Component which will display an Input on stardust. */ const Input: React.FC = ({ input, network, isPreExpanded }) => { - const history = useHistory(); const { tokenInfo } = useContext(NetworkContext); const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? true); From 7a5c838602d1f2f40bf2903f0658ac6f8aae2913 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 19:57:04 +0200 Subject: [PATCH 08/23] chore: open output by defaut (was reverted when I clean code). Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 90667bbd4..f515ca898 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -61,7 +61,7 @@ class Output extends Component { super(props); this.state = { - isExpanded: this.props.isPreExpanded ?? false, + isExpanded: this.props.isPreExpanded ?? true, isFormattedBalance: true, }; } From 3a1898f5436d3eb452f8990c04d81b5fa38ee33e Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Tue, 23 Jan 2024 21:18:29 +0200 Subject: [PATCH 09/23] chore: request details if has unlock condition expiration Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 1 + .../app/routes/stardust/TransactionPage.tsx | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index f515ca898..01a2640d9 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -72,6 +72,7 @@ class Output extends Component { */ public render(): ReactNode { const { outputId, output, amount, showCopyAmount, network, isPreExpanded, displayFullOutputId, isLinksDisabled } = this.props; + // console.log(output); const { isExpanded, isFormattedBalance } = this.state; const tokenInfo: INodeInfoBaseToken = this.context.tokenInfo; diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index b805a26c7..d6a6edc64 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -23,6 +23,10 @@ import InclusionState from "../../components/stardust/InclusionState"; import TruncatedId from "../../components/stardust/TruncatedId"; import NetworkContext from "../../context/NetworkContext"; import "./TransactionPage.scss"; +import { ServiceFactory } from "~factories/serviceFactory"; +import { StardustApiClient } from "~services/stardust/stardustApiClient"; +import { STARDUST } from "~models/config/protocolVersion"; +import { OutputResponse, UnlockConditionType } from "@iota/sdk-wasm/web"; enum TRANSACTION_PAGE_TABS { Payload = "Payload", @@ -36,6 +40,7 @@ const TransactionPage: React.FC> = ({ }, }) => { const { tokenInfo } = useContext(NetworkContext); + const [apiClient] = useState(() => ServiceFactory.get(`api-client-${STARDUST}`)); const [block, isIncludedBlockLoading, blockError] = useTransactionIncludedBlock(network, transactionId); const [inputs, unlocks, outputs, transferTotal, isInputsAndOutputsLoading] = useInputsAndOutputs(network, block); const [includedBlockId, setIncludedBlockId] = useState(null); @@ -45,6 +50,8 @@ const TransactionPage: React.FC> = ({ const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); + // console.log('--- inputs', inputs, outputs); + useEffect(() => { if (block?.payload?.type === PayloadType.Transaction) { const transactionPayload = block.payload as ITransactionPayload; @@ -55,6 +62,59 @@ const TransactionPage: React.FC> = ({ } }, [block]); + const requestOutputDetails = async (outputId: string): Promise => { + if (!outputId) return null; + + try { + const response = await apiClient.outputDetails({ network, outputId }); + const details = response.output; + + if (!response.error && details?.output && details?.metadata) { + return details; + } + return null; + } catch { + console.log("Failed loading transaction history details!"); + return null; + } + }; + + useEffect(() => { + // request output details for unlocks with expiration + if (!inputs || !outputs) return; + + (async () => { + const outputIdsWithUnlockConditions = []; + + for (const input of inputs) { + // @ts-ignore + const hasUnlockExpiration = input?.output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); + // @ts-ignore + if (hasUnlockExpiration) { + outputIdsWithUnlockConditions.push(input?.outputId); + } + } + + for (const output of outputs) { + + // @ts-ignore + const hasUnlockExpiration = output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); + + if (hasUnlockExpiration) { + outputIdsWithUnlockConditions.push(output?.id); + } + } + + if (outputIdsWithUnlockConditions.length > 0) { + const outputDetails = await Promise.all(outputIdsWithUnlockConditions.map((outputId) => { + return apiClient.outputDetails({ network, outputId }); + })); + console.log('--- outputDetails', outputDetails); + } + + })(); + }, [inputs, outputs]); + const { metadata, metadataError, conflictReason, blockTangleStatus } = blockMetadata; const isLinksDisabled = metadata?.ledgerInclusionState === "conflicting"; const isMarketed = isMarketedNetwork(network); From a2734ed44cf297cec81084ba19ff7cab31959982 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 26 Jan 2024 17:00:43 +0200 Subject: [PATCH 10/23] chore: manage expand/collapse for output through TransactionPage. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 3 +- client/src/app/components/stardust/Output.tsx | 24 +----- .../app/components/stardust/OutputProps.tsx | 5 ++ .../block/payload/TransactionPayload.tsx | 46 +++++++++- .../block/payload/TransactionPayloadProps.tsx | 2 +- .../app/routes/stardust/TransactionPage.tsx | 86 +++++++++++-------- 6 files changed, 103 insertions(+), 63 deletions(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 2b8589127..9010c4b96 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -15,7 +15,7 @@ interface InputProps { /** * The inputs. */ - readonly input: IInput; + readonly input: IInput & { unlockConditionOpenedIndexes?: number[]; }; /** * The network in context. */ @@ -94,6 +94,7 @@ const Input: React.FC = ({ input, network, isPreExpanded }) => { amount={Number(input.output.output.amount)} network={network} showCopyAmount={true} + unlockConditionOpenedIndexes={input.unlockConditionOpenedIndexes} /> ) : ( fallbackInputView diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 01a2640d9..44c100ce5 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -61,7 +61,7 @@ class Output extends Component { super(props); this.state = { - isExpanded: this.props.isPreExpanded ?? true, + isExpanded: this.props.isPreExpanded ?? false, isFormattedBalance: true, }; } @@ -225,12 +225,12 @@ class Output extends Component { {output.type !== OutputType.Treasury && ( {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { - const isExpandedByCondition = this.getExpandedStateInUnlockCondition(unlockCondition); + const isExpandedByCondition = this.props.unlockConditionOpenedIndexes && this.props.unlockConditionOpenedIndexes.includes(idx); return ( ); })} @@ -266,25 +266,7 @@ class Output extends Component { ); } - private getExpandedStateInUnlockCondition(unlockCondition?: IUnlockCondition, unlockConditions?: IUnlockCondition[]): boolean { - if (!unlockCondition) return false; - const expirationUnlockCondition = unlockConditions?.find( - (cond) => cond.type === UnlockConditionType.Expiration, - ) as ExpirationUnlockCondition; - const isExpirationConditionPresent = !!expirationUnlockCondition; - const isExpirationConditionExpired = - isExpirationConditionPresent && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); - - switch (unlockCondition.type) { - case UnlockConditionType.Address: - return !isExpirationConditionPresent || (isExpirationConditionPresent && !isExpirationConditionExpired); - case UnlockConditionType.Expiration: - return isExpirationConditionExpired; - default: - return false; - } - } /** * Build bech32 address. diff --git a/client/src/app/components/stardust/OutputProps.tsx b/client/src/app/components/stardust/OutputProps.tsx index 6c76dbe03..b0cdf796e 100644 --- a/client/src/app/components/stardust/OutputProps.tsx +++ b/client/src/app/components/stardust/OutputProps.tsx @@ -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[]; } diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index a7a023fe4..14f9dcf52 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -9,6 +9,26 @@ import Input from "../../Input"; import Output from "../../Output"; import Unlocks from "../../Unlocks"; import "./TransactionPayload.scss"; +import { DateHelper } from "~helpers/dateHelper"; +import { + AddressType, + AliasAddress, + AliasOutput, + CommonOutput, + ExpirationUnlockCondition, + FoundryOutput, + ImmutableAliasAddressUnlockCondition, + INodeInfoBaseToken, + NftOutput, + OutputType, + SimpleTokenScheme, + StorageDepositReturnUnlockCondition, + TimelockUnlockCondition, + TokenSchemeType, + UnlockCondition as IUnlockCondition, + UnlockConditionType, + Utils, +} from "@iota/sdk-wasm/web"; /** * Component which will display a transaction payload. @@ -49,7 +69,7 @@ class TransactionPayload extends AsyncComponent {header && ( @@ -69,7 +89,7 @@ class TransactionPayload extends AsyncComponent
{inputs.map((input, idx) => ( - + ))}
@@ -90,7 +110,9 @@ class TransactionPayload extends AsyncComponent ))} @@ -101,4 +123,24 @@ class TransactionPayload extends AsyncComponent cond.type === UnlockConditionType.Expiration, + ) as ExpirationUnlockCondition; + const isExpirationConditionPresent = !!expirationUnlockCondition; + const isExpirationConditionExpired = + isExpirationConditionPresent && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); + + switch (unlockCondition.type) { + case UnlockConditionType.Address: + return !isExpirationConditionPresent || (isExpirationConditionPresent && !isExpirationConditionExpired); + case UnlockConditionType.Expiration: + return isExpirationConditionExpired; + default: + return false; + } +} + export default TransactionPayload; diff --git a/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx b/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx index 8cf808b58..9fae3a02f 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx @@ -11,7 +11,7 @@ export interface TransactionPayloadProps { /** * The inputs. */ - inputs: IInput[]; + inputs: (IInput & { unlockConditionOpenedIndexes?: number[]; })[]; /** * The unlocks of the transaction. diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index d6a6edc64..709715593 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -49,8 +49,19 @@ const TransactionPage: React.FC> = ({ const [blockChildren] = useBlockChildren(network, includedBlockId); const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); + const [inputsExtraInfo, setInputsExtraInfo] = useState<{ unlockConditionOpenedIndexes?: number[]; }[]>(); - // console.log('--- inputs', inputs, outputs); + + const inputsWithExtraInfo = React.useMemo(() => { + if (!inputs) return null; + + return inputs.map((input, idx) => { + return { + ...input, + unlockConditionOpenedIndexes: [], + }; + }); + }, [inputs, inputsExtraInfo]); useEffect(() => { if (block?.payload?.type === PayloadType.Transaction) { @@ -79,41 +90,40 @@ const TransactionPage: React.FC> = ({ } }; - useEffect(() => { - // request output details for unlocks with expiration - if (!inputs || !outputs) return; - - (async () => { - const outputIdsWithUnlockConditions = []; - - for (const input of inputs) { - // @ts-ignore - const hasUnlockExpiration = input?.output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); - // @ts-ignore - if (hasUnlockExpiration) { - outputIdsWithUnlockConditions.push(input?.outputId); - } - } - - for (const output of outputs) { - - // @ts-ignore - const hasUnlockExpiration = output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); - - if (hasUnlockExpiration) { - outputIdsWithUnlockConditions.push(output?.id); - } - } - - if (outputIdsWithUnlockConditions.length > 0) { - const outputDetails = await Promise.all(outputIdsWithUnlockConditions.map((outputId) => { - return apiClient.outputDetails({ network, outputId }); - })); - console.log('--- outputDetails', outputDetails); - } - - })(); - }, [inputs, outputs]); + // useEffect(() => { + // // request output details for unlocks with expiration + // if (!inputs || !outputs) return; + // + // (async () => { + // const outputIdsWithUnlockConditions = []; + // + // for (const input of inputs) { + // // @ts-ignore + // const hasUnlockExpiration = input?.output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); + // // @ts-ignore + // if (hasUnlockExpiration) { + // outputIdsWithUnlockConditions.push(input?.outputId); + // } + // } + // + // for (const output of outputs) { + // + // // @ts-ignore + // const hasUnlockExpiration = output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); + // + // if (hasUnlockExpiration) { + // outputIdsWithUnlockConditions.push(output?.id); + // } + // } + // + // if (outputIdsWithUnlockConditions.length > 0) { + // const outputDetails = await Promise.all(outputIdsWithUnlockConditions.map((outputId) => { + // return apiClient.outputDetails({ network, outputId }); + // })); + // } + // + // })(); + // }, [inputs, outputs]); const { metadata, metadataError, conflictReason, blockTangleStatus } = blockMetadata; const isLinksDisabled = metadata?.ledgerInclusionState === "conflicting"; @@ -214,9 +224,9 @@ const TransactionPage: React.FC> = ({ }, }} > - {inputs && unlocks && outputs ? ( + {inputsWithExtraInfo && unlocks && outputs ? (
- +
) : ( <> From 11470da9ac131d3b54626e3996f808256bac0dc5 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 26 Jan 2024 21:55:59 +0200 Subject: [PATCH 11/23] chore: detect unlock condition in input. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 1 + client/src/app/components/stardust/Output.tsx | 15 ++-- .../components/stardust/UnlockCondition.tsx | 10 +-- .../block/payload/TransactionPayload.tsx | 30 +++---- .../app/routes/stardust/TransactionPage.tsx | 83 ++++++++++++++----- 5 files changed, 95 insertions(+), 44 deletions(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 9010c4b96..2950766d3 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -30,6 +30,7 @@ interface InputProps { * Component which will display an Input on stardust. */ const Input: React.FC = ({ input, network, isPreExpanded }) => { + console.log('--- input', input); const history = useHistory(); const { tokenInfo } = useContext(NetworkContext); const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? true); diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 44c100ce5..88fdd608b 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -225,13 +225,18 @@ class Output extends Component { {output.type !== OutputType.Treasury && ( {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { + const isExpandedByCondition = this.props.unlockConditionOpenedIndexes && this.props.unlockConditionOpenedIndexes.includes(idx); + console.log('--- isExpandedByCondition', isExpandedByCondition); return ( - + <> + {isExpandedByCondition ? 'true' : 'false'} + + ); })} {(output as CommonOutput).features?.map((feature, idx) => ( diff --git a/client/src/app/components/stardust/UnlockCondition.tsx b/client/src/app/components/stardust/UnlockCondition.tsx index c4dacd149..b13433128 100644 --- a/client/src/app/components/stardust/UnlockCondition.tsx +++ b/client/src/app/components/stardust/UnlockCondition.tsx @@ -10,7 +10,7 @@ import { UnlockConditionType, } from "@iota/sdk-wasm/web"; import classNames from "classnames"; -import React, { ReactNode } from "react"; +import React, { ReactNode, Component } from "react"; import DropdownIcon from "~assets/dropdown-arrow.svg?react"; import Address from "./address/Address"; import { UnlockConditionProps } from "./UnlockConditionProps"; @@ -24,7 +24,7 @@ import AsyncComponent from "../AsyncComponent"; /** * Component which will display an unlock condition. */ -class UnlockCondition extends AsyncComponent { +class UnlockCondition extends Component { /** * The component context type. */ @@ -47,9 +47,9 @@ class UnlockCondition extends AsyncComponent { - super.componentDidMount(); - } + // public async componentDidMount(): Promise { + // super.componentDidMount(); + // } /** * Render the component. diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index 14f9dcf52..2ca9e4286 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -11,23 +11,23 @@ import Unlocks from "../../Unlocks"; import "./TransactionPayload.scss"; import { DateHelper } from "~helpers/dateHelper"; import { - AddressType, - AliasAddress, - AliasOutput, - CommonOutput, + // AddressType, + // AliasAddress, + // AliasOutput, + // CommonOutput, ExpirationUnlockCondition, - FoundryOutput, - ImmutableAliasAddressUnlockCondition, - INodeInfoBaseToken, - NftOutput, - OutputType, - SimpleTokenScheme, - StorageDepositReturnUnlockCondition, - TimelockUnlockCondition, - TokenSchemeType, + // FoundryOutput, + // ImmutableAliasAddressUnlockCondition, + // INodeInfoBaseToken, + // NftOutput, + // OutputType, + // SimpleTokenScheme, + // StorageDepositReturnUnlockCondition, + // TimelockUnlockCondition, + // TokenSchemeType, UnlockCondition as IUnlockCondition, UnlockConditionType, - Utils, + // Utils, } from "@iota/sdk-wasm/web"; /** @@ -69,7 +69,7 @@ class TransactionPayload extends AsyncComponent {header && ( diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 709715593..1135bbb7a 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/jsx-no-useless-fragment */ -import { PayloadType, RegularTransactionEssence, TransactionPayload as ITransactionPayload, Utils } from "@iota/sdk-wasm/web"; +import { PayloadType, RegularTransactionEssence, TransactionPayload as ITransactionPayload, Utils, UnlockConditionType, CommonOutput } from "@iota/sdk-wasm/web"; import React, { useContext, useEffect, useState } from "react"; import { RouteComponentProps } from "react-router-dom"; import { TransactionPageProps } from "./TransactionPageProps"; @@ -26,8 +26,7 @@ import "./TransactionPage.scss"; import { ServiceFactory } from "~factories/serviceFactory"; import { StardustApiClient } from "~services/stardust/stardustApiClient"; import { STARDUST } from "~models/config/protocolVersion"; -import { OutputResponse, UnlockConditionType } from "@iota/sdk-wasm/web"; - +import { IInput } from "~models/api/stardust/IInput"; enum TRANSACTION_PAGE_TABS { Payload = "Payload", BlockMetadata = "Block Metadata", @@ -49,16 +48,17 @@ const TransactionPage: React.FC> = ({ const [blockChildren] = useBlockChildren(network, includedBlockId); const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); - const [inputsExtraInfo, setInputsExtraInfo] = useState<{ unlockConditionOpenedIndexes?: number[]; }[]>(); + const [inputsExtraInfo, setInputsExtraInfo] = useState<{ [outputId: string]: {unlockConditionOpenedIndexes?: number[];} }>({}); const inputsWithExtraInfo = React.useMemo(() => { if (!inputs) return null; - + console.log('--- inputsExtraInfo', inputsExtraInfo); return inputs.map((input, idx) => { + const extraInfo = inputsExtraInfo[input.outputId]; return { ...input, - unlockConditionOpenedIndexes: [], + unlockConditionOpenedIndexes: extraInfo?.unlockConditionOpenedIndexes ?? [], }; }); }, [inputs, inputsExtraInfo]); @@ -73,22 +73,48 @@ const TransactionPage: React.FC> = ({ } }, [block]); - const requestOutputDetails = async (outputId: string): Promise => { - if (!outputId) return null; + useEffect(() => { + if (!inputs) return; + + inputs.forEach(input => { - try { - const response = await apiClient.outputDetails({ network, outputId }); - const details = response.output; + if (isExpirationExists(input)) { - if (!response.error && details?.output && details?.metadata) { - return details; + } else { + const indexes = getIndexesAddressUnlockCondition(input); + setInputsExtraInfo({ + ...inputsExtraInfo, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + }); } - return null; - } catch { - console.log("Failed loading transaction history details!"); - return null; - } - }; + + }); + // const spentInputIds = + + // If input with expiration condition - check if spent. + // If spent - check when. + + + }, [inputs]); + + // const requestOutputDetails = async (outputId: string): Promise => { + // if (!outputId) return null; + // + // try { + // const response = await apiClient.outputDetails({ network, outputId }); + // const details = response.output; + // + // if (!response.error && details?.output && details?.metadata) { + // return details; + // } + // return null; + // } catch { + // console.log("Failed loading transaction history details!"); + // return null; + // } + // }; // useEffect(() => { // // request output details for unlocks with expiration @@ -311,4 +337,23 @@ const TransactionPage: React.FC> = ({ ); }; +function getIndexesAddressUnlockCondition(input: IInput) { + const output = input.output?.output as CommonOutput; + if (!output?.unlockConditions) return []; + + return output.unlockConditions.map((i, idx) => { + if (i.type === UnlockConditionType.Address) { + return idx; + } + }).filter(i => i !== undefined) as number[]; +} + +function isExpirationExists(input: IInput) { + const output = input.output?.output as CommonOutput; + if (!output?.unlockConditions) return false; + + return output.unlockConditions.some(i => i.type === UnlockConditionType.Expiration); +} + + export default TransactionPage; From 35e5f080e3491b1b140101e7543e0287d09ddf0c Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Sun, 28 Jan 2024 13:34:41 +0200 Subject: [PATCH 12/23] chore: update unlock condition state based on props. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 15 +- .../components/stardust/UnlockCondition.tsx | 182 ++++++++++-------- 2 files changed, 106 insertions(+), 91 deletions(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 88fdd608b..2e50b98ec 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -227,16 +227,13 @@ class Output extends Component { {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { const isExpandedByCondition = this.props.unlockConditionOpenedIndexes && this.props.unlockConditionOpenedIndexes.includes(idx); - console.log('--- isExpandedByCondition', isExpandedByCondition); + return ( - <> - {isExpandedByCondition ? 'true' : 'false'} - - + ); })} {(output as CommonOutput).features?.map((feature, idx) => ( diff --git a/client/src/app/components/stardust/UnlockCondition.tsx b/client/src/app/components/stardust/UnlockCondition.tsx index b13433128..2d0a4c5ed 100644 --- a/client/src/app/components/stardust/UnlockCondition.tsx +++ b/client/src/app/components/stardust/UnlockCondition.tsx @@ -10,7 +10,7 @@ import { UnlockConditionType, } from "@iota/sdk-wasm/web"; import classNames from "classnames"; -import React, { ReactNode, Component } from "react"; +import React, { ReactNode, Component, useContext, useState, useEffect } from "react"; import DropdownIcon from "~assets/dropdown-arrow.svg?react"; import Address from "./address/Address"; import { UnlockConditionProps } from "./UnlockConditionProps"; @@ -19,30 +19,31 @@ 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"; +// import AsyncComponent from "../AsyncComponent"; /** * Component which will display an unlock condition. */ -class UnlockCondition extends Component { +// function UnlockCondition extends Component { +function UnlockCondition(props: UnlockConditionProps) { /** * The component context type. */ - public static contextType = NetworkContext; + // public static contextType = NetworkContext; /** * The component context. */ - public declare context: React.ContextType; + // public declare context: React.ContextType; - constructor(props: UnlockConditionProps) { - super(props); - - this.state = { - isFormattedBalance: true, - isExpanded: this.props.isPreExpanded ?? false, - }; - } + // constructor(props: UnlockConditionProps) { + // super(props); + // + // this.state = { + // + // + // }; + // } /** * The component mounted. @@ -55,78 +56,95 @@ class UnlockCondition extends Component({ + isFormattedBalance: true, + isExpanded: props.isPreExpanded ?? false, + }); + const context = useContext(NetworkContext); + const { isFormattedBalance, isExpanded } = state; + const { unlockCondition } = props; + const tokenInfo: INodeInfoBaseToken = context.tokenInfo; + + // This useEffect hook synchronizes the `isExpanded` state with the `isPreExpanded` prop. + // It updates `isExpanded` only when `isPreExpanded` changes and is not null/undefined. + // This ensures that the component reacts to relevant changes from the parent component + // while maintaining its own internal state. + useEffect(() => { + if (props.isPreExpanded !== undefined && props.isPreExpanded !== null) { + setState(prevState => ({ + ...prevState, + isExpanded: props.isPreExpanded ?? false + })); + } + }, [props.isPreExpanded]); - return ( -
-
this.setState({ isExpanded: !isExpanded })}> -
- -
-
{NameHelper.getUnlockConditionTypeName(unlockCondition.type)}
+ return ( +
+
setState((p) => ({ ...p, isExpanded: !isExpanded }))}> +
+
- {isExpanded && ( -
- {unlockCondition.type === UnlockConditionType.Address && ( -
- )} - {unlockCondition.type === UnlockConditionType.StorageDepositReturn && ( - -
Return address
-
-
Amount:
-
- this.setState({ isFormattedBalance: !isFormattedBalance })} - > - {formatAmount( - Number((unlockCondition as StorageDepositReturnUnlockCondition).amount), - tokenInfo, - !isFormattedBalance, - )} - -
- - )} - {unlockCondition.type === UnlockConditionType.Timelock && (unlockCondition as TimelockUnlockCondition).unixTime && ( - -
Unix time
-
- {DateHelper.formatShort((unlockCondition as TimelockUnlockCondition).unixTime * 1000)} -
-
- )} - {unlockCondition.type === UnlockConditionType.Expiration && ( - -
- {(unlockCondition as ExpirationUnlockCondition).unixTime && ( - -
Unix time
-
- {DateHelper.formatShort((unlockCondition as ExpirationUnlockCondition).unixTime * 1000)} -
-
- )} - - )} - {unlockCondition.type === UnlockConditionType.GovernorAddress && ( -
- )} - {unlockCondition.type === UnlockConditionType.ImmutableAliasAddress && ( -
- )} - {unlockCondition.type === UnlockConditionType.StateControllerAddress && ( -
- )} -
- )} +
{NameHelper.getUnlockConditionTypeName(unlockCondition.type)}
- ); - } + {isExpanded && ( +
+ {unlockCondition.type === UnlockConditionType.Address && ( +
+ )} + {unlockCondition.type === UnlockConditionType.StorageDepositReturn && ( + +
Return address
+
+
Amount:
+
+ setState((p) => ({ ...p, isFormattedBalance: !isFormattedBalance }))} + > + {formatAmount( + Number((unlockCondition as StorageDepositReturnUnlockCondition).amount), + tokenInfo, + !isFormattedBalance, + )} + +
+ + )} + {unlockCondition.type === UnlockConditionType.Timelock && (unlockCondition as TimelockUnlockCondition).unixTime && ( + +
Unix time
+
+ {DateHelper.formatShort((unlockCondition as TimelockUnlockCondition).unixTime * 1000)} +
+
+ )} + {unlockCondition.type === UnlockConditionType.Expiration && ( + +
+ {(unlockCondition as ExpirationUnlockCondition).unixTime && ( + +
Unix time
+
+ {DateHelper.formatShort((unlockCondition as ExpirationUnlockCondition).unixTime * 1000)} +
+
+ )} + + )} + {unlockCondition.type === UnlockConditionType.GovernorAddress && ( +
+ )} + {unlockCondition.type === UnlockConditionType.ImmutableAliasAddress && ( +
+ )} + {unlockCondition.type === UnlockConditionType.StateControllerAddress && ( +
+ )} +
+ )} +
+ ); } export default UnlockCondition; From 0864291e2b489663da054b51eb8d93a6a15f4b25 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Sun, 28 Jan 2024 20:09:05 +0200 Subject: [PATCH 13/23] chore: add conditions if expiration exists. Signed-off-by: Eugene Panteleymonchuk --- .../app/routes/stardust/TransactionPage.tsx | 68 +++++++++++++++---- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 1135bbb7a..f67fd8827 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -27,6 +27,9 @@ import { ServiceFactory } from "~factories/serviceFactory"; import { StardustApiClient } from "~services/stardust/stardustApiClient"; import { STARDUST } from "~models/config/protocolVersion"; import { IInput } from "~models/api/stardust/IInput"; +import { OutputResponse, ExpirationUnlockCondition } from "@iota/sdk-wasm/web"; +import { DateHelper } from "~helpers/dateHelper"; + enum TRANSACTION_PAGE_TABS { Payload = "Payload", BlockMetadata = "Block Metadata", @@ -74,23 +77,43 @@ const TransactionPage: React.FC> = ({ }, [block]); useEffect(() => { - if (!inputs) return; + (async () => { + if (!inputs) return; - inputs.forEach(input => { + inputs.forEach(input => { - if (isExpirationExists(input)) { + if (isExpirationExists(input)) { + if (isOutputSpent(input)) { - } else { - const indexes = getIndexesAddressUnlockCondition(input); - setInputsExtraInfo({ - ...inputsExtraInfo, - [input.outputId]: { - unlockConditionOpenedIndexes: indexes, - }, - }); - } + } else { + const expirationUnlockCondition = getUnlockConditionByType(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); + let indexes: number[] = []; + if (isExpired) { + indexes = getUnlockConditionIndexesByType(input, UnlockConditionType.Expiration); + } else { + indexes = getUnlockConditionIndexesByType(input, UnlockConditionType.Address); + } - }); + setInputsExtraInfo({ + ...inputsExtraInfo, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + }); + } + } else { + const indexes = getUnlockConditionIndexesByType(input, UnlockConditionType.Address); + setInputsExtraInfo({ + ...inputsExtraInfo, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + }); + } + + }); + })(); // const spentInputIds = // If input with expiration condition - check if spent. @@ -337,17 +360,32 @@ const TransactionPage: React.FC> = ({ ); }; -function getIndexesAddressUnlockCondition(input: IInput) { + + +function isOutputSpent(input: IInput) { + const output = input.output as OutputResponse; + return output.metadata.isSpent; +} + +function getUnlockConditionByType(input: IInput, type: UnlockConditionType) { + const output = input.output?.output as CommonOutput; + if (!output?.unlockConditions) return null; + + return output.unlockConditions.find(i => i.type === type); +} + +function getUnlockConditionIndexesByType(input: IInput, type: UnlockConditionType) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return []; return output.unlockConditions.map((i, idx) => { - if (i.type === UnlockConditionType.Address) { + if (i.type === type) { return idx; } }).filter(i => i !== undefined) as number[]; } +// If expiration unlock condition exists - return true. function isExpirationExists(input: IInput) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return false; From 7a83772871642491172235de47f9434cdbcdc5c5 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Sun, 28 Jan 2024 20:19:41 +0200 Subject: [PATCH 14/23] chore: update by settimeout to make sure that update rerender component. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 1 - .../app/routes/stardust/TransactionPage.tsx | 33 ++++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 2950766d3..9010c4b96 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -30,7 +30,6 @@ interface InputProps { * Component which will display an Input on stardust. */ const Input: React.FC = ({ input, network, isPreExpanded }) => { - console.log('--- input', input); const history = useHistory(); const { tokenInfo } = useContext(NetworkContext); const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? true); diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index f67fd8827..1805541fc 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -51,12 +51,13 @@ const TransactionPage: React.FC> = ({ const [blockChildren] = useBlockChildren(network, includedBlockId); const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); - const [inputsExtraInfo, setInputsExtraInfo] = useState<{ [outputId: string]: {unlockConditionOpenedIndexes?: number[];} }>({}); + const [inputsExtraInfo, setInputsExtraInfo] = useState<{ [outputId: string]: {unlockConditionOpenedIndexes?: number[];} }>({ + + }); const inputsWithExtraInfo = React.useMemo(() => { if (!inputs) return null; - console.log('--- inputsExtraInfo', inputsExtraInfo); return inputs.map((input, idx) => { const extraInfo = inputsExtraInfo[input.outputId]; return { @@ -77,22 +78,36 @@ const TransactionPage: React.FC> = ({ }, [block]); useEffect(() => { + setTimeout(() => { + setInputsExtraInfo({ + '0x2c8eb84d31b6e419a2bf0da89d4a70484590a7228e6c1a3d4c130f0d9b02bb140000': { + unlockConditionOpenedIndexes: [0] + } + }); + console.log('--- update'); + }, 5000) + }, []); + + useEffect(() => { + return; (async () => { if (!inputs) return; inputs.forEach(input => { if (isExpirationExists(input)) { - if (isOutputSpent(input)) { + console.log('--- input.outputId', input.outputId); + if (!isOutputSpent(input)) { } else { - const expirationUnlockCondition = getUnlockConditionByType(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); + // debugger; let indexes: number[] = []; if (isExpired) { - indexes = getUnlockConditionIndexesByType(input, UnlockConditionType.Expiration); + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); } else { - indexes = getUnlockConditionIndexesByType(input, UnlockConditionType.Address); + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); } setInputsExtraInfo({ @@ -103,7 +118,7 @@ const TransactionPage: React.FC> = ({ }); } } else { - const indexes = getUnlockConditionIndexesByType(input, UnlockConditionType.Address); + const indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); setInputsExtraInfo({ ...inputsExtraInfo, [input.outputId]: { @@ -367,14 +382,14 @@ function isOutputSpent(input: IInput) { return output.metadata.isSpent; } -function getUnlockConditionByType(input: IInput, type: UnlockConditionType) { +function getUnlockCondition(input: IInput, type: UnlockConditionType) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return null; return output.unlockConditions.find(i => i.type === type); } -function getUnlockConditionIndexesByType(input: IInput, type: UnlockConditionType) { +function getUnlockConditionIndexes(input: IInput, type: UnlockConditionType) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return []; From f0ef39abd8d31fadd745474d871cdd34c428d292 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Sun, 28 Jan 2024 21:12:27 +0200 Subject: [PATCH 15/23] chore: complete all conditions. Signed-off-by: Eugene Panteleymonchuk --- .../app/routes/stardust/TransactionPage.tsx | 57 ++++++++++++------- client/src/helpers/dateHelper.ts | 5 +- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 1805541fc..285078b4a 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -77,32 +77,47 @@ const TransactionPage: React.FC> = ({ } }, [block]); - useEffect(() => { - setTimeout(() => { - setInputsExtraInfo({ - '0x2c8eb84d31b6e419a2bf0da89d4a70484590a7228e6c1a3d4c130f0d9b02bb140000': { - unlockConditionOpenedIndexes: [0] - } - }); - console.log('--- update'); - }, 5000) - }, []); + // useEffect(() => { + // setTimeout(() => { + // setInputsExtraInfo({ + // '0x2c8eb84d31b6e419a2bf0da89d4a70484590a7228e6c1a3d4c130f0d9b02bb140000': { + // unlockConditionOpenedIndexes: [0] + // } + // }); + // console.log('--- update'); + // }, 5000) + // }, []); useEffect(() => { - return; (async () => { if (!inputs) return; inputs.forEach(input => { if (isExpirationExists(input)) { - console.log('--- input.outputId', input.outputId); - if (!isOutputSpent(input)) { + if (isOutputSpent(input)) { + // pass + const milestoneTimestamp = input?.output?.metadata?.milestoneTimestampSpent as number; + + + const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const isSpentAfterUnlock = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000, milestoneTimestamp * 1000); + let indexes: number[] = []; + if (isSpentAfterUnlock) { + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); + } else { + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); + } + setInputsExtraInfo((prev) => ({ + ...prev, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + })); } else { const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); - // debugger; let indexes: number[] = []; if (isExpired) { indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); @@ -110,21 +125,21 @@ const TransactionPage: React.FC> = ({ indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); } - setInputsExtraInfo({ - ...inputsExtraInfo, + setInputsExtraInfo((prev) => ({ + ...prev, [input.outputId]: { unlockConditionOpenedIndexes: indexes, }, - }); + })); } } else { const indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); - setInputsExtraInfo({ - ...inputsExtraInfo, + setInputsExtraInfo((prev) => ({ + ...prev, [input.outputId]: { unlockConditionOpenedIndexes: indexes, }, - }); + })); } }); @@ -135,7 +150,7 @@ const TransactionPage: React.FC> = ({ // If spent - check when. - }, [inputs]); + }, [inputs, setInputsExtraInfo]); // const requestOutputDetails = async (outputId: string): Promise => { // if (!outputId) return null; diff --git a/client/src/helpers/dateHelper.ts b/client/src/helpers/dateHelper.ts index 03650b604..f1cd843fc 100644 --- a/client/src/helpers/dateHelper.ts +++ b/client/src/helpers/dateHelper.ts @@ -42,10 +42,11 @@ export class DateHelper { /** * Check if the date is expired. * @param valueInMs The value to format in milliseconds. + * @param compareTime * @returns Boolean if the date is expired. */ - public static isExpired(valueInMs: number): boolean { - return moment(valueInMs).isBefore(moment()); + public static isExpired(valueInMs: number, compareTime?: number): boolean { + return moment(valueInMs).isBefore(compareTime ? moment(compareTime) : moment()); } /** From ed0d13b95ecfbda32c25f488ff5280825582fbcb Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Sun, 28 Jan 2024 21:45:15 +0200 Subject: [PATCH 16/23] chore: request timestamp for transactionSpentId. Signed-off-by: Eugene Panteleymonchuk --- .../app/routes/stardust/TransactionPage.tsx | 196 +++++++----------- 1 file changed, 78 insertions(+), 118 deletions(-) diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 285078b4a..1e5e1a21a 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -29,6 +29,7 @@ import { STARDUST } from "~models/config/protocolVersion"; import { IInput } from "~models/api/stardust/IInput"; import { OutputResponse, ExpirationUnlockCondition } from "@iota/sdk-wasm/web"; import { DateHelper } from "~helpers/dateHelper"; +import { HexHelper } from "~helpers/stardust/hexHelper"; enum TRANSACTION_PAGE_TABS { Payload = "Payload", @@ -77,133 +78,70 @@ const TransactionPage: React.FC> = ({ } }, [block]); - // useEffect(() => { - // setTimeout(() => { - // setInputsExtraInfo({ - // '0x2c8eb84d31b6e419a2bf0da89d4a70484590a7228e6c1a3d4c130f0d9b02bb140000': { - // unlockConditionOpenedIndexes: [0] - // } - // }); - // console.log('--- update'); - // }, 5000) - // }, []); + const updateInputExtraInfo = async (input: IInput) => { + if (isExpirationExists(input)) { + if (isOutputSpent(input)) { + const transactionSpentId = input?.output?.metadata.transactionIdSpent as string; + const transactionTimestamp = await getTransactionTimestamp(transactionSpentId, apiClient, network); + + const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const isSpentAfterUnlock = + expirationUnlockCondition && + transactionTimestamp && + DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000, transactionTimestamp); + + let indexes: number[] = []; + if (isSpentAfterUnlock) { + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); + } else { + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); + } + setInputsExtraInfo((prev) => ({ + ...prev, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + })); + + } else { + const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); + let indexes: number[] = []; + if (isExpired) { + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); + } else { + indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); + } + + setInputsExtraInfo((prev) => ({ + ...prev, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + })); + } + } else { + const indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); + setInputsExtraInfo((prev) => ({ + ...prev, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + })); + } + } useEffect(() => { (async () => { if (!inputs) return; - inputs.forEach(input => { - - if (isExpirationExists(input)) { - if (isOutputSpent(input)) { - // pass - const milestoneTimestamp = input?.output?.metadata?.milestoneTimestampSpent as number; - - - const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; - const isSpentAfterUnlock = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000, milestoneTimestamp * 1000); - let indexes: number[] = []; - if (isSpentAfterUnlock) { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); - } else { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); - } - setInputsExtraInfo((prev) => ({ - ...prev, - [input.outputId]: { - unlockConditionOpenedIndexes: indexes, - }, - })); - - } else { - const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; - const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); - let indexes: number[] = []; - if (isExpired) { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); - } else { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); - } - - setInputsExtraInfo((prev) => ({ - ...prev, - [input.outputId]: { - unlockConditionOpenedIndexes: indexes, - }, - })); - } - } else { - const indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); - setInputsExtraInfo((prev) => ({ - ...prev, - [input.outputId]: { - unlockConditionOpenedIndexes: indexes, - }, - })); - } - - }); + await Promise.all(inputs.map(input => { + updateInputExtraInfo(input); + })); })(); - // const spentInputIds = - - // If input with expiration condition - check if spent. - // If spent - check when. - }, [inputs, setInputsExtraInfo]); - // const requestOutputDetails = async (outputId: string): Promise => { - // if (!outputId) return null; - // - // try { - // const response = await apiClient.outputDetails({ network, outputId }); - // const details = response.output; - // - // if (!response.error && details?.output && details?.metadata) { - // return details; - // } - // return null; - // } catch { - // console.log("Failed loading transaction history details!"); - // return null; - // } - // }; - - // useEffect(() => { - // // request output details for unlocks with expiration - // if (!inputs || !outputs) return; - // - // (async () => { - // const outputIdsWithUnlockConditions = []; - // - // for (const input of inputs) { - // // @ts-ignore - // const hasUnlockExpiration = input?.output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); - // // @ts-ignore - // if (hasUnlockExpiration) { - // outputIdsWithUnlockConditions.push(input?.outputId); - // } - // } - // - // for (const output of outputs) { - // - // // @ts-ignore - // const hasUnlockExpiration = output?.output?.unlockConditions?.some(i => i.type === UnlockConditionType.Expiration); - // - // if (hasUnlockExpiration) { - // outputIdsWithUnlockConditions.push(output?.id); - // } - // } - // - // if (outputIdsWithUnlockConditions.length > 0) { - // const outputDetails = await Promise.all(outputIdsWithUnlockConditions.map((outputId) => { - // return apiClient.outputDetails({ network, outputId }); - // })); - // } - // - // })(); - // }, [inputs, outputs]); - const { metadata, metadataError, conflictReason, blockTangleStatus } = blockMetadata; const isLinksDisabled = metadata?.ledgerInclusionState === "conflicting"; const isMarketed = isMarketedNetwork(network); @@ -423,5 +361,27 @@ function isExpirationExists(input: IInput) { return output.unlockConditions.some(i => i.type === UnlockConditionType.Expiration); } +async function getTransactionTimestamp(transactionId: string, apiClient: StardustApiClient, network: string) { + const blockResp = await apiClient.transactionIncludedBlockDetails({ network, transactionId }); + + if (blockResp?.block?.payload?.type !== PayloadType.Transaction) return null; + + const includedBlockId = Utils.blockId(blockResp.block); + + const blockMetadataResp = await apiClient.blockDetails({ + network, + blockId: HexHelper.addPrefix(includedBlockId), + }); + + const blockMetadata = blockMetadataResp?.metadata; + const referencedByMilestoneIndex = blockMetadata?.referencedByMilestoneIndex; + if (referencedByMilestoneIndex === undefined) return null; + + const milestoneResp = await apiClient.milestoneDetails({ network, milestoneIndex: referencedByMilestoneIndex }); + if (!milestoneResp?.milestone?.timestamp) return null; + + return DateHelper.milliseconds(milestoneResp.milestone.timestamp); +} + export default TransactionPage; From ecf421d9018848be44bd41e4c0fbbc869c681bc3 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Sun, 28 Jan 2024 21:49:31 +0200 Subject: [PATCH 17/23] fix: eslint + prettier. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Input.tsx | 2 +- client/src/app/components/stardust/Output.tsx | 6 +-- .../components/stardust/UnlockCondition.tsx | 15 +++--- .../block/payload/TransactionPayload.tsx | 40 ---------------- .../block/payload/TransactionPayloadProps.tsx | 2 +- .../app/routes/stardust/TransactionPage.tsx | 46 ++++++++++--------- 6 files changed, 37 insertions(+), 74 deletions(-) diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 9010c4b96..593d9a33a 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -15,7 +15,7 @@ interface InputProps { /** * The inputs. */ - readonly input: IInput & { unlockConditionOpenedIndexes?: number[]; }; + readonly input: IInput & { unlockConditionOpenedIndexes?: number[] }; /** * The network in context. */ diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 2e50b98ec..6a72deee7 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -225,8 +225,8 @@ class Output extends Component { {output.type !== OutputType.Treasury && ( {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { - - const isExpandedByCondition = this.props.unlockConditionOpenedIndexes && this.props.unlockConditionOpenedIndexes.includes(idx); + const isExpandedByCondition = + this.props.unlockConditionOpenedIndexes && this.props.unlockConditionOpenedIndexes.includes(idx); return ( { ); } - - /** * Build bech32 address. * @returns The bech32 address. diff --git a/client/src/app/components/stardust/UnlockCondition.tsx b/client/src/app/components/stardust/UnlockCondition.tsx index 2d0a4c5ed..459c7d0e3 100644 --- a/client/src/app/components/stardust/UnlockCondition.tsx +++ b/client/src/app/components/stardust/UnlockCondition.tsx @@ -10,7 +10,7 @@ import { UnlockConditionType, } from "@iota/sdk-wasm/web"; import classNames from "classnames"; -import React, { ReactNode, Component, useContext, useState, useEffect } 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"; @@ -58,8 +58,8 @@ function UnlockCondition(props: UnlockConditionProps) { */ // public render(): ReactNode { const [state, setState] = useState({ - isFormattedBalance: true, - isExpanded: props.isPreExpanded ?? false, + isFormattedBalance: true, + isExpanded: props.isPreExpanded ?? false, }); const context = useContext(NetworkContext); const { isFormattedBalance, isExpanded } = state; @@ -72,16 +72,19 @@ function UnlockCondition(props: UnlockConditionProps) { // while maintaining its own internal state. useEffect(() => { if (props.isPreExpanded !== undefined && props.isPreExpanded !== null) { - setState(prevState => ({ + setState((prevState) => ({ ...prevState, - isExpanded: props.isPreExpanded ?? false + isExpanded: props.isPreExpanded ?? false, })); } }, [props.isPreExpanded]); return (
-
setState((p) => ({ ...p, isExpanded: !isExpanded }))}> +
setState((p) => ({ ...p, isExpanded: !isExpanded }))} + >
diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index 2ca9e4286..bcfbc0b45 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -9,26 +9,6 @@ import Input from "../../Input"; import Output from "../../Output"; import Unlocks from "../../Unlocks"; import "./TransactionPayload.scss"; -import { DateHelper } from "~helpers/dateHelper"; -import { - // AddressType, - // AliasAddress, - // AliasOutput, - // CommonOutput, - ExpirationUnlockCondition, - // FoundryOutput, - // ImmutableAliasAddressUnlockCondition, - // INodeInfoBaseToken, - // NftOutput, - // OutputType, - // SimpleTokenScheme, - // StorageDepositReturnUnlockCondition, - // TimelockUnlockCondition, - // TokenSchemeType, - UnlockCondition as IUnlockCondition, - UnlockConditionType, - // Utils, -} from "@iota/sdk-wasm/web"; /** * Component which will display a transaction payload. @@ -123,24 +103,4 @@ class TransactionPayload extends AsyncComponent cond.type === UnlockConditionType.Expiration, - ) as ExpirationUnlockCondition; - const isExpirationConditionPresent = !!expirationUnlockCondition; - const isExpirationConditionExpired = - isExpirationConditionPresent && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); - - switch (unlockCondition.type) { - case UnlockConditionType.Address: - return !isExpirationConditionPresent || (isExpirationConditionPresent && !isExpirationConditionExpired); - case UnlockConditionType.Expiration: - return isExpirationConditionExpired; - default: - return false; - } -} - export default TransactionPayload; diff --git a/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx b/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx index 9fae3a02f..d5d8b4623 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx @@ -11,7 +11,7 @@ export interface TransactionPayloadProps { /** * The inputs. */ - inputs: (IInput & { unlockConditionOpenedIndexes?: number[]; })[]; + inputs: (IInput & { unlockConditionOpenedIndexes?: number[] })[]; /** * The unlocks of the transaction. diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 1e5e1a21a..3f6008b12 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -1,5 +1,12 @@ /* eslint-disable react/jsx-no-useless-fragment */ -import { PayloadType, RegularTransactionEssence, TransactionPayload as ITransactionPayload, Utils, UnlockConditionType, CommonOutput } from "@iota/sdk-wasm/web"; +import { + PayloadType, + RegularTransactionEssence, + TransactionPayload as ITransactionPayload, + Utils, + UnlockConditionType, + CommonOutput, +} from "@iota/sdk-wasm/web"; import React, { useContext, useEffect, useState } from "react"; import { RouteComponentProps } from "react-router-dom"; import { TransactionPageProps } from "./TransactionPageProps"; @@ -52,10 +59,7 @@ const TransactionPage: React.FC> = ({ const [blockChildren] = useBlockChildren(network, includedBlockId); const [blockMetadata, isBlockMetadataLoading] = useBlockMetadata(network, includedBlockId); const [isFormattedBalance, setIsFormattedBalance] = useState(true); - const [inputsExtraInfo, setInputsExtraInfo] = useState<{ [outputId: string]: {unlockConditionOpenedIndexes?: number[];} }>({ - - }); - + const [inputsExtraInfo, setInputsExtraInfo] = useState<{ [outputId: string]: { unlockConditionOpenedIndexes?: number[] } }>({}); const inputsWithExtraInfo = React.useMemo(() => { if (!inputs) return null; @@ -102,7 +106,6 @@ const TransactionPage: React.FC> = ({ unlockConditionOpenedIndexes: indexes, }, })); - } else { const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); @@ -129,17 +132,18 @@ const TransactionPage: React.FC> = ({ }, })); } - } + }; useEffect(() => { (async () => { if (!inputs) return; - await Promise.all(inputs.map(input => { - updateInputExtraInfo(input); - })); + await Promise.all( + inputs.map((input) => { + updateInputExtraInfo(input); + }), + ); })(); - }, [inputs, setInputsExtraInfo]); const { metadata, metadataError, conflictReason, blockTangleStatus } = blockMetadata; @@ -328,8 +332,6 @@ const TransactionPage: React.FC> = ({ ); }; - - function isOutputSpent(input: IInput) { const output = input.output as OutputResponse; return output.metadata.isSpent; @@ -339,26 +341,27 @@ function getUnlockCondition(input: IInput, type: UnlockConditionType) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return null; - return output.unlockConditions.find(i => i.type === type); + return output.unlockConditions.find((i) => i.type === type); } function getUnlockConditionIndexes(input: IInput, type: UnlockConditionType) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return []; - return output.unlockConditions.map((i, idx) => { - if (i.type === type) { - return idx; - } - }).filter(i => i !== undefined) as number[]; + return output.unlockConditions + .map((i, idx) => { + if (i.type === type) { + return idx; + } + }) + .filter((i) => i !== undefined) as number[]; } -// If expiration unlock condition exists - return true. function isExpirationExists(input: IInput) { const output = input.output?.output as CommonOutput; if (!output?.unlockConditions) return false; - return output.unlockConditions.some(i => i.type === UnlockConditionType.Expiration); + return output.unlockConditions.some((i) => i.type === UnlockConditionType.Expiration); } async function getTransactionTimestamp(transactionId: string, apiClient: StardustApiClient, network: string) { @@ -383,5 +386,4 @@ async function getTransactionTimestamp(transactionId: string, apiClient: Stardus return DateHelper.milliseconds(milestoneResp.milestone.timestamp); } - export default TransactionPage; From 4dc9464fe454e4be7109ebdcd515c34c00be9a63 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Mon, 29 Jan 2024 16:30:42 +0200 Subject: [PATCH 18/23] chore: refactoring to simplify if/else conditions. Signed-off-by: Eugene Panteleymonchuk --- .../app/routes/stardust/TransactionPage.tsx | 106 +++++++++--------- client/src/helpers/dateHelper.ts | 8 +- 2 files changed, 56 insertions(+), 58 deletions(-) diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 3f6008b12..c438f9ef3 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -72,58 +72,8 @@ const TransactionPage: React.FC> = ({ }); }, [inputs, inputsExtraInfo]); - useEffect(() => { - if (block?.payload?.type === PayloadType.Transaction) { - const transactionPayload = block.payload as ITransactionPayload; - const transactionEssence = transactionPayload.essence as RegularTransactionEssence; - setIncludedBlockId(Utils.blockId(block)); - setTangleNetworkId(transactionEssence.networkId); - setInputsCommitment(transactionEssence.inputsCommitment); - } - }, [block]); - const updateInputExtraInfo = async (input: IInput) => { - if (isExpirationExists(input)) { - if (isOutputSpent(input)) { - const transactionSpentId = input?.output?.metadata.transactionIdSpent as string; - const transactionTimestamp = await getTransactionTimestamp(transactionSpentId, apiClient, network); - - const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; - const isSpentAfterUnlock = - expirationUnlockCondition && - transactionTimestamp && - DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000, transactionTimestamp); - - let indexes: number[] = []; - if (isSpentAfterUnlock) { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); - } else { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); - } - setInputsExtraInfo((prev) => ({ - ...prev, - [input.outputId]: { - unlockConditionOpenedIndexes: indexes, - }, - })); - } else { - const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; - const isExpired = expirationUnlockCondition && DateHelper.isExpired(expirationUnlockCondition.unixTime * 1000); - let indexes: number[] = []; - if (isExpired) { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Expiration); - } else { - indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); - } - - setInputsExtraInfo((prev) => ({ - ...prev, - [input.outputId]: { - unlockConditionOpenedIndexes: indexes, - }, - })); - } - } else { + if (!isExpirationExists(input)) { const indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); setInputsExtraInfo((prev) => ({ ...prev, @@ -131,9 +81,57 @@ const TransactionPage: React.FC> = ({ unlockConditionOpenedIndexes: indexes, }, })); + return; + } + + const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; + const expirationConditionTimestamp = expirationUnlockCondition.unixTime * 1000; + + if (!isOutputSpent(input)) { + const isExpired = DateHelper.isExpired(expirationConditionTimestamp); + const indexes: number[] = isExpired ? + getUnlockConditionIndexes(input, UnlockConditionType.Expiration) : + getUnlockConditionIndexes(input, UnlockConditionType.Address); + + setInputsExtraInfo((prev) => ({ + ...prev, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + })); + return; + } + + const transactionSpentId = input?.output?.metadata.transactionIdSpent as string; + const transactionTimestamp = await getTransactionTimestamp(transactionSpentId, apiClient, network); + + if (!transactionTimestamp) { + console.error(`Failed to get transaction timestamp for ${transactionSpentId}`); } + const isSpentAfterUnlock = DateHelper.isExpired(expirationConditionTimestamp, transactionTimestamp); + + const indexes = isSpentAfterUnlock ? + getUnlockConditionIndexes(input, UnlockConditionType.Expiration) : + getUnlockConditionIndexes(input, UnlockConditionType.Address); + + setInputsExtraInfo((prev) => ({ + ...prev, + [input.outputId]: { + unlockConditionOpenedIndexes: indexes, + }, + })); }; + useEffect(() => { + if (block?.payload?.type === PayloadType.Transaction) { + const transactionPayload = block.payload as ITransactionPayload; + const transactionEssence = transactionPayload.essence as RegularTransactionEssence; + setIncludedBlockId(Utils.blockId(block)); + setTangleNetworkId(transactionEssence.networkId); + setInputsCommitment(transactionEssence.inputsCommitment); + } + }, [block]); + useEffect(() => { (async () => { if (!inputs) return; @@ -367,7 +365,7 @@ function isExpirationExists(input: IInput) { async function getTransactionTimestamp(transactionId: string, apiClient: StardustApiClient, network: string) { const blockResp = await apiClient.transactionIncludedBlockDetails({ network, transactionId }); - if (blockResp?.block?.payload?.type !== PayloadType.Transaction) return null; + if (blockResp?.block?.payload?.type !== PayloadType.Transaction) return; const includedBlockId = Utils.blockId(blockResp.block); @@ -378,10 +376,10 @@ async function getTransactionTimestamp(transactionId: string, apiClient: Stardus const blockMetadata = blockMetadataResp?.metadata; const referencedByMilestoneIndex = blockMetadata?.referencedByMilestoneIndex; - if (referencedByMilestoneIndex === undefined) return null; + if (referencedByMilestoneIndex === undefined) return; const milestoneResp = await apiClient.milestoneDetails({ network, milestoneIndex: referencedByMilestoneIndex }); - if (!milestoneResp?.milestone?.timestamp) return null; + if (!milestoneResp?.milestone?.timestamp) return; return DateHelper.milliseconds(milestoneResp.milestone.timestamp); } diff --git a/client/src/helpers/dateHelper.ts b/client/src/helpers/dateHelper.ts index f1cd843fc..a07db5957 100644 --- a/client/src/helpers/dateHelper.ts +++ b/client/src/helpers/dateHelper.ts @@ -41,12 +41,12 @@ export class DateHelper { /** * Check if the date is expired. - * @param valueInMs The value to format in milliseconds. - * @param compareTime + * @param value The value to check in milliseconds. + * @param compareWith * @returns Boolean if the date is expired. */ - public static isExpired(valueInMs: number, compareTime?: number): boolean { - return moment(valueInMs).isBefore(compareTime ? moment(compareTime) : moment()); + public static isExpired(value: number, compareWith: number = Date.now()): boolean { + return moment(moment(compareWith)).isAfter(value); } /** From 12a93e88a8f25049af64907bf68062883c0c7178 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Mon, 29 Jan 2024 17:09:10 +0200 Subject: [PATCH 19/23] chore: add same conditions for outputs. Signed-off-by: Eugene Panteleymonchuk --- .../block/payload/TransactionPayload.tsx | 4 +- .../block/payload/TransactionPayloadProps.tsx | 2 +- .../app/routes/stardust/TransactionPage.tsx | 50 ++++++++++++++++--- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index bcfbc0b45..dd736158d 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -49,7 +49,7 @@ class TransactionPayload extends AsyncComponent {header && ( @@ -92,7 +92,7 @@ class TransactionPayload extends AsyncComponent ))}
diff --git a/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx b/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx index d5d8b4623..bd277bb66 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayloadProps.tsx @@ -21,7 +21,7 @@ export interface TransactionPayloadProps { /** * The outputs. */ - outputs: IOutput[]; + outputs: (IOutput & { unlockConditionOpenedIndexes?: number[] })[]; /** * The header title of this section. diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index c438f9ef3..0ace81c5b 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -37,6 +37,7 @@ import { IInput } from "~models/api/stardust/IInput"; import { OutputResponse, ExpirationUnlockCondition } from "@iota/sdk-wasm/web"; import { DateHelper } from "~helpers/dateHelper"; import { HexHelper } from "~helpers/stardust/hexHelper"; +import { IOutput } from "~models/api/stardust/IOutput"; enum TRANSACTION_PAGE_TABS { Payload = "Payload", @@ -61,6 +62,19 @@ const TransactionPage: React.FC> = ({ const [isFormattedBalance, setIsFormattedBalance] = useState(true); const [inputsExtraInfo, setInputsExtraInfo] = useState<{ [outputId: string]: { unlockConditionOpenedIndexes?: number[] } }>({}); + const outputsWithExtraInfo = React.useMemo(() => { + if (!outputs) return null; + return outputs.map((output, idx) => { + const extraInfo = inputsExtraInfo[output.id]; + console.log('--- extraInfo', extraInfo); + return { + ...output, + unlockConditionOpenedIndexes: extraInfo?.unlockConditionOpenedIndexes ?? [], + }; + }); + }, [outputs, inputsExtraInfo]); + + const inputsWithExtraInfo = React.useMemo(() => { if (!inputs) return null; return inputs.map((input, idx) => { @@ -86,12 +100,13 @@ const TransactionPage: React.FC> = ({ const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; const expirationConditionTimestamp = expirationUnlockCondition.unixTime * 1000; + const commonOutput = input.output?.output as CommonOutput; if (!isOutputSpent(input)) { const isExpired = DateHelper.isExpired(expirationConditionTimestamp); const indexes: number[] = isExpired ? - getUnlockConditionIndexes(input, UnlockConditionType.Expiration) : - getUnlockConditionIndexes(input, UnlockConditionType.Address); + getUnlockConditionIndexes(commonOutput, UnlockConditionType.Expiration) : + getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); setInputsExtraInfo((prev) => ({ ...prev, @@ -111,8 +126,8 @@ const TransactionPage: React.FC> = ({ const isSpentAfterUnlock = DateHelper.isExpired(expirationConditionTimestamp, transactionTimestamp); const indexes = isSpentAfterUnlock ? - getUnlockConditionIndexes(input, UnlockConditionType.Expiration) : - getUnlockConditionIndexes(input, UnlockConditionType.Address); + getUnlockConditionIndexes(commonOutput, UnlockConditionType.Expiration) : + getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); setInputsExtraInfo((prev) => ({ ...prev, @@ -122,6 +137,17 @@ const TransactionPage: React.FC> = ({ })); }; + const updateOutputExtraInfo = async (output: IOutput) => { + const indexes = getUnlockConditionIndexes(output?.output as CommonOutput, UnlockConditionType.Address); + console.log('--- indexes', indexes); + setInputsExtraInfo((prev) => ({ + ...prev, + [output.id]: { + unlockConditionOpenedIndexes: indexes, + }, + })); + }; + useEffect(() => { if (block?.payload?.type === PayloadType.Transaction) { const transactionPayload = block.payload as ITransactionPayload; @@ -143,6 +169,15 @@ const TransactionPage: React.FC> = ({ ); })(); }, [inputs, setInputsExtraInfo]); + useEffect(() => { + (async () => { + if (!outputs) return; + + outputs.map((output) => { + updateOutputExtraInfo(output); + }); + })(); + }, [outputs, setInputsExtraInfo]); const { metadata, metadataError, conflictReason, blockTangleStatus } = blockMetadata; const isLinksDisabled = metadata?.ledgerInclusionState === "conflicting"; @@ -243,9 +278,9 @@ const TransactionPage: React.FC> = ({ }, }} > - {inputsWithExtraInfo && unlocks && outputs ? ( + {inputsWithExtraInfo && unlocks && outputsWithExtraInfo ? (
- +
) : ( <> @@ -342,8 +377,7 @@ function getUnlockCondition(input: IInput, type: UnlockConditionType) { return output.unlockConditions.find((i) => i.type === type); } -function getUnlockConditionIndexes(input: IInput, type: UnlockConditionType) { - const output = input.output?.output as CommonOutput; +function getUnlockConditionIndexes(output: CommonOutput, type: UnlockConditionType) { if (!output?.unlockConditions) return []; return output.unlockConditions From c436792c034218e5483d3c9170992fe082d4f40e Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Mon, 29 Jan 2024 17:11:37 +0200 Subject: [PATCH 20/23] fix: eslint. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/Output.tsx | 1 - .../block/payload/TransactionPayload.tsx | 1 - .../app/routes/stardust/TransactionPage.tsx | 22 ++++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 6a72deee7..0aed12d61 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -72,7 +72,6 @@ class Output extends Component { */ public render(): ReactNode { const { outputId, output, amount, showCopyAmount, network, isPreExpanded, displayFullOutputId, isLinksDisabled } = this.props; - // console.log(output); const { isExpanded, isFormattedBalance } = this.state; const tokenInfo: INodeInfoBaseToken = this.context.tokenInfo; diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index dd736158d..10e1a5f1e 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -49,7 +49,6 @@ class TransactionPayload extends AsyncComponent {header && ( diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 0ace81c5b..91c2ad191 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -66,7 +66,6 @@ const TransactionPage: React.FC> = ({ if (!outputs) return null; return outputs.map((output, idx) => { const extraInfo = inputsExtraInfo[output.id]; - console.log('--- extraInfo', extraInfo); return { ...output, unlockConditionOpenedIndexes: extraInfo?.unlockConditionOpenedIndexes ?? [], @@ -74,7 +73,6 @@ const TransactionPage: React.FC> = ({ }); }, [outputs, inputsExtraInfo]); - const inputsWithExtraInfo = React.useMemo(() => { if (!inputs) return null; return inputs.map((input, idx) => { @@ -104,9 +102,9 @@ const TransactionPage: React.FC> = ({ if (!isOutputSpent(input)) { const isExpired = DateHelper.isExpired(expirationConditionTimestamp); - const indexes: number[] = isExpired ? - getUnlockConditionIndexes(commonOutput, UnlockConditionType.Expiration) : - getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); + const indexes: number[] = isExpired + ? getUnlockConditionIndexes(commonOutput, UnlockConditionType.Expiration) + : getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); setInputsExtraInfo((prev) => ({ ...prev, @@ -125,9 +123,9 @@ const TransactionPage: React.FC> = ({ } const isSpentAfterUnlock = DateHelper.isExpired(expirationConditionTimestamp, transactionTimestamp); - const indexes = isSpentAfterUnlock ? - getUnlockConditionIndexes(commonOutput, UnlockConditionType.Expiration) : - getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); + const indexes = isSpentAfterUnlock + ? getUnlockConditionIndexes(commonOutput, UnlockConditionType.Expiration) + : getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); setInputsExtraInfo((prev) => ({ ...prev, @@ -139,7 +137,6 @@ const TransactionPage: React.FC> = ({ const updateOutputExtraInfo = async (output: IOutput) => { const indexes = getUnlockConditionIndexes(output?.output as CommonOutput, UnlockConditionType.Address); - console.log('--- indexes', indexes); setInputsExtraInfo((prev) => ({ ...prev, [output.id]: { @@ -280,7 +277,12 @@ const TransactionPage: React.FC> = ({ > {inputsWithExtraInfo && unlocks && outputsWithExtraInfo ? (
- +
) : ( <> From 36a0e5348acd79bf6555cad557d970e2c7d68a19 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Mon, 29 Jan 2024 17:15:48 +0200 Subject: [PATCH 21/23] fix: ts Signed-off-by: Eugene Panteleymonchuk --- client/src/app/routes/stardust/TransactionPage.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/src/app/routes/stardust/TransactionPage.tsx b/client/src/app/routes/stardust/TransactionPage.tsx index 91c2ad191..14092e3b5 100644 --- a/client/src/app/routes/stardust/TransactionPage.tsx +++ b/client/src/app/routes/stardust/TransactionPage.tsx @@ -64,7 +64,7 @@ const TransactionPage: React.FC> = ({ const outputsWithExtraInfo = React.useMemo(() => { if (!outputs) return null; - return outputs.map((output, idx) => { + return outputs.map((output) => { const extraInfo = inputsExtraInfo[output.id]; return { ...output, @@ -75,7 +75,7 @@ const TransactionPage: React.FC> = ({ const inputsWithExtraInfo = React.useMemo(() => { if (!inputs) return null; - return inputs.map((input, idx) => { + return inputs.map((input) => { const extraInfo = inputsExtraInfo[input.outputId]; return { ...input, @@ -85,8 +85,10 @@ const TransactionPage: React.FC> = ({ }, [inputs, inputsExtraInfo]); const updateInputExtraInfo = async (input: IInput) => { + const commonOutput = input.output?.output as CommonOutput; + if (!isExpirationExists(input)) { - const indexes = getUnlockConditionIndexes(input, UnlockConditionType.Address); + const indexes = getUnlockConditionIndexes(commonOutput, UnlockConditionType.Address); setInputsExtraInfo((prev) => ({ ...prev, [input.outputId]: { @@ -98,7 +100,6 @@ const TransactionPage: React.FC> = ({ const expirationUnlockCondition = getUnlockCondition(input, UnlockConditionType.Expiration) as ExpirationUnlockCondition; const expirationConditionTimestamp = expirationUnlockCondition.unixTime * 1000; - const commonOutput = input.output?.output as CommonOutput; if (!isOutputSpent(input)) { const isExpired = DateHelper.isExpired(expirationConditionTimestamp); From 3e4f801cc50821916dd5296b3c6c2f8752748b4f Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Mon, 29 Jan 2024 18:41:10 +0200 Subject: [PATCH 22/23] chore: remove comments from UnlockCondition component. Signed-off-by: Eugene Panteleymonchuk --- .../components/stardust/UnlockCondition.tsx | 38 +------------------ 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/client/src/app/components/stardust/UnlockCondition.tsx b/client/src/app/components/stardust/UnlockCondition.tsx index 459c7d0e3..09c061267 100644 --- a/client/src/app/components/stardust/UnlockCondition.tsx +++ b/client/src/app/components/stardust/UnlockCondition.tsx @@ -19,44 +19,11 @@ 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. */ -// function UnlockCondition extends Component { function UnlockCondition(props: UnlockConditionProps) { - /** - * The component context type. - */ - // public static contextType = NetworkContext; - - /** - * The component context. - */ - // public declare context: React.ContextType; - - // constructor(props: UnlockConditionProps) { - // super(props); - // - // this.state = { - // - // - // }; - // } - - /** - * The component mounted. - */ - // public async componentDidMount(): Promise { - // super.componentDidMount(); - // } - - /** - * Render the component. - * @returns The node to render. - */ - // public render(): ReactNode { const [state, setState] = useState({ isFormattedBalance: true, isExpanded: props.isPreExpanded ?? false, @@ -66,10 +33,7 @@ function UnlockCondition(props: UnlockConditionProps) { const { unlockCondition } = props; const tokenInfo: INodeInfoBaseToken = context.tokenInfo; - // This useEffect hook synchronizes the `isExpanded` state with the `isPreExpanded` prop. - // It updates `isExpanded` only when `isPreExpanded` changes and is not null/undefined. - // This ensures that the component reacts to relevant changes from the parent component - // while maintaining its own internal state. + useEffect(() => { if (props.isPreExpanded !== undefined && props.isPreExpanded !== null) { setState((prevState) => ({ From eb9647e80852426d0a273f67e8cb00eb533d9409 Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Mon, 29 Jan 2024 18:44:08 +0200 Subject: [PATCH 23/23] fix: eslint. Signed-off-by: Eugene Panteleymonchuk --- client/src/app/components/stardust/UnlockCondition.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/app/components/stardust/UnlockCondition.tsx b/client/src/app/components/stardust/UnlockCondition.tsx index 09c061267..d158966ca 100644 --- a/client/src/app/components/stardust/UnlockCondition.tsx +++ b/client/src/app/components/stardust/UnlockCondition.tsx @@ -33,7 +33,6 @@ function UnlockCondition(props: UnlockConditionProps) { const { unlockCondition } = props; const tokenInfo: INodeInfoBaseToken = context.tokenInfo; - useEffect(() => { if (props.isPreExpanded !== undefined && props.isPreExpanded !== null) { setState((prevState) => ({