Skip to content

Commit 70410ef

Browse files
authored
[Invoke Contract] Show only the results by default; add 'show full response' toggle (#1533)
1 parent a327710 commit 70410ef

File tree

3 files changed

+107
-34
lines changed

3 files changed

+107
-34
lines changed

src/app/(sidebar)/smart-contracts/contract-explorer/components/InvokeContractForm.tsx

Lines changed: 97 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import {
1111
Button,
1212
Card,
1313
Icon,
14+
Label,
1415
Text,
1516
Textarea,
1617
Tooltip,
18+
Toggle,
1719
} from "@stellar/design-system";
1820
import { BASE_FEE, contract } from "@stellar/stellar-sdk";
1921
import { Api } from "@stellar/stellar-sdk/rpc";
@@ -47,25 +49,9 @@ import { SorobanInvokeValue, XdrFormatType, AnyObject } from "@/types/types";
4749

4850
import { trackEvent, TrackingEvent } from "@/metrics/tracking";
4951

50-
export const SimulatedResponse = ({
51-
result,
52-
}: {
53-
result: Api.SimulateTransactionResponse;
54-
}) => {
55-
const errorClass = Api.isSimulationError(result)
56-
? "PageBody__content--error"
57-
: "";
58-
59-
return (
60-
<Box gap="md">
61-
<div
62-
data-testid="invoke-contract-simulate-tx-response"
63-
className={`PageBody__content PageBody__scrollable ${errorClass}`}
64-
>
65-
<PrettyJson json={result} isCodeWrapped={true} />
66-
</div>
67-
</Box>
68-
);
52+
type SimulatedResponseType = {
53+
fullResponse: Api.SimulateTransactionResponse;
54+
resultOnly: AnyObject;
6955
};
7056

7157
export const InvokeContractForm = ({
@@ -92,10 +78,10 @@ export const InvokeContractForm = ({
9278
const [formError, setFormError] = useState<AnyObject>({});
9379
const [isGetFunction, setIsGetFunction] = useState(false);
9480

95-
const [jsonResult, setJsonResult] =
96-
useState<Api.SimulateTransactionResponse>();
97-
const [base64Result, setBase64Result] =
98-
useState<Api.SimulateTransactionResponse>();
81+
const [jsonResponse, setJsonResponse] = useState<SimulatedResponseType>();
82+
const [base64Response, setBase64Response] = useState<SimulatedResponseType>();
83+
const [isFullResponseEnabled, setIsFullResponseEnabled] =
84+
useState<boolean>(false);
9985

10086
// Based on reads and writes to the contract
10187
// Can only be determined based on the simulation result
@@ -384,7 +370,7 @@ export const InvokeContractForm = ({
384370

385371
const handleSimulate = async (xdr: string) => {
386372
try {
387-
const [jsonResult, base64Result] = await Promise.all([
373+
const [simJsonResponse, simBase64Response] = await Promise.all([
388374
simulateTx({
389375
rpcUrl: network.rpcUrl,
390376
transactionXdr: xdr,
@@ -399,12 +385,18 @@ export const InvokeContractForm = ({
399385
}),
400386
]);
401387

402-
if (jsonResult?.result) {
403-
setJsonResult(jsonResult?.result);
388+
if (simJsonResponse?.result) {
389+
setJsonResponse({
390+
fullResponse: simJsonResponse?.result,
391+
resultOnly: simJsonResponse?.result?.results,
392+
});
404393
}
405394

406-
if (base64Result?.result) {
407-
setBase64Result(base64Result?.result);
395+
if (simBase64Response?.result) {
396+
setBase64Response({
397+
fullResponse: simBase64Response?.result,
398+
resultOnly: simBase64Response?.result?.results,
399+
});
408400
}
409401

410402
trackEvent(
@@ -589,22 +581,30 @@ export const InvokeContractForm = ({
589581
};
590582

591583
const renderResponse = () => {
592-
if (jsonResult || base64Result) {
584+
if (jsonResponse?.fullResponse || base64Response?.fullResponse) {
593585
return (
594586
<TabView
595587
tab1={{
596588
id: "json",
597589
label: "JSON",
598-
content: jsonResult && <SimulatedResponse result={jsonResult} />,
599-
isDisabled: !jsonResult,
590+
content: jsonResponse?.fullResponse && (
591+
<SimulatedResponse
592+
result={jsonResponse}
593+
isFullResponseEnabled={isFullResponseEnabled}
594+
/>
595+
),
596+
isDisabled: !jsonResponse?.fullResponse,
600597
}}
601598
tab2={{
602599
id: "base64",
603600
label: "Base64",
604-
content: base64Result && (
605-
<SimulatedResponse result={base64Result} />
601+
content: base64Response?.fullResponse && (
602+
<SimulatedResponse
603+
result={base64Response}
604+
isFullResponseEnabled={isFullResponseEnabled}
605+
/>
606606
),
607-
isDisabled: !base64Result,
607+
isDisabled: !base64Response?.fullResponse,
608608
}}
609609
activeTabId={xdrFormat}
610610
onTabChange={(id) => {
@@ -616,6 +616,13 @@ export const InvokeContractForm = ({
616616
},
617617
);
618618
}}
619+
rightElement={
620+
<FullResponseToggle
621+
isChecked={isFullResponseEnabled}
622+
onChange={setIsFullResponseEnabled}
623+
id={funcName}
624+
/>
625+
}
619626
/>
620627
);
621628
}
@@ -734,3 +741,59 @@ export const InvokeContractForm = ({
734741
</Card>
735742
);
736743
};
744+
745+
export const FullResponseToggle = ({
746+
isChecked,
747+
onChange,
748+
id,
749+
}: {
750+
isChecked: boolean;
751+
onChange: (isChecked: boolean) => void;
752+
id: string;
753+
}) => {
754+
return (
755+
<Box gap="sm" direction="row" align="center">
756+
<Label htmlFor="full-response-toggle" size="sm">
757+
Show full response
758+
</Label>
759+
<Toggle
760+
id={`full-response-toggle-${id}`}
761+
fieldSize="sm"
762+
title="Show full response"
763+
onChange={() => {
764+
const newValue = !isChecked;
765+
766+
onChange(newValue);
767+
}}
768+
checked={isChecked}
769+
/>
770+
</Box>
771+
);
772+
};
773+
774+
export const SimulatedResponse = ({
775+
result,
776+
isFullResponseEnabled = false,
777+
}: {
778+
result: SimulatedResponseType;
779+
isFullResponseEnabled: boolean;
780+
}) => {
781+
const errorClass = Api.isSimulationError(result.fullResponse)
782+
? "PageBody__content--error"
783+
: "";
784+
785+
const json = isFullResponseEnabled
786+
? result.fullResponse
787+
: (result?.resultOnly && { results: result?.resultOnly }) || {};
788+
789+
return (
790+
<Box gap="md">
791+
<div
792+
data-testid="invoke-contract-simulate-tx-response"
793+
className={`PageBody__content PageBody__scrollable ${errorClass}`}
794+
>
795+
<PrettyJson json={json} isCodeWrapped={true} />
796+
</div>
797+
</Box>
798+
);
799+
};

src/components/TabView/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ type TabViewProps = {
2424
tab6?: Tab;
2525
onTabChange: (id: string) => void;
2626
activeTabId: string;
27+
rightElement?: React.ReactNode;
2728
};
2829

2930
export const TabView = ({
3031
heading,
3132
onTabChange,
3233
activeTabId,
34+
rightElement,
3335
...tabs
3436
}: TabViewProps) => {
3537
const tabItems = Object.values(tabs).map((t) => ({
@@ -55,6 +57,10 @@ export const TabView = ({
5557
onChange={onTabChange}
5658
/>
5759
</div>
60+
61+
{rightElement ? (
62+
<div className="TabView__rightElement">{rightElement}</div>
63+
) : null}
5864
</div>
5965

6066
<div className="TabView__content">

src/components/TabView/styles.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@
1818
display: block;
1919
}
2020
}
21+
22+
&__rightElement {
23+
align-self: flex-end;
24+
}
2125
}

0 commit comments

Comments
 (0)