Skip to content

Commit

Permalink
feat: separate approve and perform buttons (#257)
Browse files Browse the repository at this point in the history
* feat: separate approve and perform buttons

* chore: delete test build output

* fix: set allowed flag only in one place
  • Loading branch information
tnrdd committed Oct 11, 2022
1 parent 2d51e76 commit aed464f
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import * as React from "react";
import Button from "react-bootstrap/Button";
import Spinner from "react-bootstrap/Spinner";
import Image from "react-bootstrap/Image";
import { BigNumber, constants } from "ethers";
import { SidebarProps } from "./Sidebar";
import { PAYMENT_TOKEN } from "../lib/constants";

export type ApproveOrPerformButtonProps = SidebarProps & {
export type ApproveButtonProps = SidebarProps & {
isDisabled: boolean;
performAction: () => Promise<string | void>;
buttonText: string;
requiredPayment: BigNumber | null;
requiredFlowAmount: BigNumber | null;
requiredFlowPermissions: number | null;
Expand All @@ -17,6 +16,8 @@ export type ApproveOrPerformButtonProps = SidebarProps & {
setErrorMessage: (v: string) => void;
setIsActing: (v: boolean) => void;
setDidFail: (v: boolean) => void;
isAllowed: boolean;
setIsAllowed: React.Dispatch<React.SetStateAction<boolean>>;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -27,11 +28,9 @@ const asyncEvery = async (arr: any, predicate: any) => {
return true;
};

export function ApproveOrPerformButton(props: ApproveOrPerformButtonProps) {
export function ApproveButton(props: ApproveButtonProps) {
const {
isDisabled,
performAction,
buttonText,
paymentToken,
account,
spender,
Expand All @@ -44,12 +43,14 @@ export function ApproveOrPerformButton(props: ApproveOrPerformButtonProps) {
setErrorMessage,
setIsActing,
setDidFail,
isAllowed,
setIsAllowed,
} = props;

const [approvals, setApprovals] = React.useState<(() => Promise<boolean>)[]>(
[]
);
const [approvalStr, setApprovalStr] = React.useState<string>(buttonText);
const [approvalStr, setApprovalStr] = React.useState<string>("");
const [completedActions, setCompletedActions] = React.useState<number>(0);
const [totalActions, setTotalActions] = React.useState<number>(0);

Expand Down Expand Up @@ -132,7 +133,7 @@ export function ApproveOrPerformButton(props: ApproveOrPerformButtonProps) {
React.useEffect(() => {
const checkRequirements = async () => {
const _approvals: (() => Promise<boolean>)[] = [];
let _approvalStr = buttonText;
let _approvalStr = `Allow ${PAYMENT_TOKEN} Transfer`;

if (!isReady) {
setApprovals(_approvals);
Expand Down Expand Up @@ -175,6 +176,12 @@ export function ApproveOrPerformButton(props: ApproveOrPerformButtonProps) {
: `Allow ${PAYMENT_TOKEN} Stream`;
}

if (_approvals.length === 0) {
setIsAllowed(true);
} else {
setIsAllowed(false);
}

setApprovals(_approvals);
setApprovalStr(_approvalStr);
};
Expand All @@ -194,30 +201,38 @@ export function ApproveOrPerformButton(props: ApproveOrPerformButtonProps) {
if (approvals.length > 0) {
setTotalActions(approvals.length);
setCompletedActions(0);
setApprovalStr(buttonText);
let _completedActions = 0;
await asyncEvery(approvals, async (f: () => Promise<boolean>) => {
const success = await f();
setCompletedActions(++_completedActions);
return success;
});
} else {
setTotalActions(1);
setCompletedActions(0);
const isSubmissionSuccessful = await asyncEvery(
approvals,
async (f: () => Promise<boolean>) => {
const success = await f();
setCompletedActions(++_completedActions);
return success;
}
);

await performAction();
setTotalActions(0);
if (!isSubmissionSuccessful) {
setCompletedActions(0);
setTotalActions(0);
}
}
}, [approvals, completedActions, buttonText]);
}, [approvals, completedActions]);

return (
<Button
variant="primary"
className="w-100"
variant={isAllowed ? "info" : "primary"}
className="w-100 mb-3"
onClick={() => submit()}
disabled={isDisabled || totalActions > completedActions || !isReady}
disabled={
isDisabled || !isReady || isAllowed || totalActions > completedActions
}
>
{totalActions > completedActions ? (
{isAllowed ? (
<>
<span className="ms-4">{PAYMENT_TOKEN} use allowed</span>
<Image src="./task-done.svg" className="float-end"></Image>
</>
) : totalActions > completedActions ? (
<>
{spinner} {`${completedActions}/${totalActions}`}
</>
Expand All @@ -228,4 +243,4 @@ export function ApproveOrPerformButton(props: ApproveOrPerformButtonProps) {
);
}

export default ApproveOrPerformButton;
export default ApproveButton;
44 changes: 44 additions & 0 deletions components/PerformButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from "react";
import Button from "react-bootstrap/Button";
import Spinner from "react-bootstrap/Spinner";

export type PerformButtonProps = {
isDisabled: boolean;
isAllowed: boolean;
isActing: boolean;
performAction: () => Promise<string | void>;
buttonText: string;
};

export function PerformButton(props: PerformButtonProps) {
const { isDisabled, isAllowed, isActing, performAction, buttonText } = props;

const submit = React.useCallback(async () => {
await performAction();
}, [performAction]);

const spinner = (
<Spinner as="span" size="sm" animation="border" role="status">
<span className="visually-hidden">Sending Transaction...</span>
</Spinner>
);

return (
<Button
variant={
!isAllowed
? "info"
: buttonText === "Reject Bid"
? "danger"
: "success"
}
className="w-100"
onClick={() => submit()}
disabled={isDisabled || !isAllowed}
>
{isActing ? spinner : buttonText}
</Button>
);
}

export default PerformButton;
37 changes: 23 additions & 14 deletions components/cards/ActionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import { model as GeoWebModel, BasicProfile } from "@geo-web/datamodels";
import { DataModel } from "@glazed/datamodel";
import { AssetContentManager } from "../../lib/AssetContentManager";
import TransactionError from "./TransactionError";
import { ApproveOrPerformButton } from "../ApproveOrPerformButton";
import ApproveButton from "../ApproveButton";
import PerformButton from "../PerformButton";

export type ActionFormProps = SidebarProps & {
perSecondFeeNumerator: BigNumber;
Expand Down Expand Up @@ -90,6 +91,7 @@ export function ActionForm(props: ActionFormProps) {
errorMessage,
} = actionData;
const [showWrapModal, setShowWrapModal] = React.useState(false);
const [isAllowed, setIsAllowed] = React.useState(false);

const handleWrapModalOpen = () => setShowWrapModal(true);
const handleWrapModalClose = () => setShowWrapModal(false);
Expand Down Expand Up @@ -377,22 +379,13 @@ export function ActionForm(props: ActionFormProps) {
className="w-100 mb-3"
onClick={handleWrapModalOpen}
>
{`Wrap to ${PAYMENT_TOKEN}`}
{`Wrap ETH to ${PAYMENT_TOKEN}`}
</Button>
<ApproveOrPerformButton
<ApproveButton
{...props}
isDisabled={isActing || isLoading || isInvalid}
buttonText={
interactionState === STATE.PARCEL_EDITING
? "Submit"
: interactionState === STATE.PARCEL_RECLAIMING &&
account.toLowerCase() === licenseOwner?.toLowerCase()
? "Reclaim"
: "Claim"
}
requiredFlowAmount={requiredFlowAmount}
isDisabled={isActing ?? false}
requiredFlowAmount={requiredFlowAmount ?? null}
requiredPayment={requiredPayment ?? null}
performAction={submit}
spender={spender ?? null}
setErrorMessage={(v) => {
updateActionData({ errorMessage: v });
Expand All @@ -403,6 +396,22 @@ export function ActionForm(props: ActionFormProps) {
setDidFail={(v) => {
updateActionData({ didFail: v });
}}
isAllowed={isAllowed}
setIsAllowed={setIsAllowed}
/>
<PerformButton
isDisabled={isActing || isLoading || isInvalid}
isActing={isActing ?? false}
buttonText={
interactionState === STATE.PARCEL_EDITING
? "Submit"
: interactionState === STATE.PARCEL_RECLAIMING &&
account.toLowerCase() === licenseOwner?.toLowerCase()
? "Reclaim"
: "Claim"
}
performAction={submit}
isAllowed={isAllowed}
/>
</Form>

Expand Down
31 changes: 21 additions & 10 deletions components/cards/PlaceBidAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { STATE } from "../Map";
import InfoTooltip from "../InfoTooltip";
import TransactionError from "./TransactionError";
import type { PCOLicenseDiamond } from "@geo-web/contracts/dist/typechain-types/PCOLicenseDiamond";
import { ApproveOrPerformButton } from "../ApproveOrPerformButton";
import { ApproveButton } from "../ApproveButton";
import { PerformButton } from "../PerformButton";
import { GeoWebParcel } from "./ParcelInfo";

export type PlaceBidActionProps = SidebarProps & {
Expand Down Expand Up @@ -55,8 +56,10 @@ function PlaceBidAction(props: PlaceBidActionProps) {
const [didFail, setDidFail] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState("");
const [isActing, setIsActing] = React.useState(false);
const [displayNewForSalePrice, setDisplayNewForSalePrice] =
React.useState<string | null>(null);
const [displayNewForSalePrice, setDisplayNewForSalePrice] = React.useState<
string | null
>(null);
const [isAllowed, setIsAllowed] = React.useState(false);

const handleWrapModalOpen = () => setShowWrapModal(true);
const handleWrapModalClose = () => setShowWrapModal(false);
Expand Down Expand Up @@ -109,8 +112,9 @@ function PlaceBidAction(props: PlaceBidActionProps) {

const isInvalid = isForSalePriceInvalid || !displayNewForSalePrice;

const [requiredBuffer, setRequiredBuffer] =
React.useState<BigNumber | null>(null);
const [requiredBuffer, setRequiredBuffer] = React.useState<BigNumber | null>(
null
);
React.useEffect(() => {
const run = async () => {
if (!newNetworkFee) {
Expand Down Expand Up @@ -273,25 +277,32 @@ function PlaceBidAction(props: PlaceBidActionProps) {
className="w-100 mb-3"
onClick={handleWrapModalOpen}
>
{`Wrap to ${PAYMENT_TOKEN}`}
{`Wrap ETH to ${PAYMENT_TOKEN}`}
</Button>
<ApproveOrPerformButton
<ApproveButton
{...props}
isDisabled={isActing || isInvalid}
buttonText={"Place Bid"}
isDisabled={isActing}
requiredFlowAmount={annualNetworkFeeRate ?? null}
requiredPayment={
newForSalePrice && requiredBuffer
? newForSalePrice.add(requiredBuffer)
: null
}
performAction={placeBid}
spender={licenseDiamondContract?.address ?? null}
requiredFlowPermissions={1}
flowOperator={licenseDiamondContract?.address ?? null}
setErrorMessage={setErrorMessage}
setIsActing={setIsActing}
setDidFail={setDidFail}
isAllowed={isAllowed}
setIsAllowed={setIsAllowed}
/>
<PerformButton
isDisabled={isActing || isInvalid}
isActing={isActing}
buttonText={"Place Bid"}
performAction={placeBid}
isAllowed={isAllowed}
/>
</Form>

Expand Down
21 changes: 15 additions & 6 deletions components/cards/RejectBidAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import { STATE } from "../Map";
import InfoTooltip from "../InfoTooltip";
import TransactionError from "./TransactionError";
import type { PCOLicenseDiamond } from "@geo-web/contracts/dist/typechain-types/PCOLicenseDiamond";
import { ApproveOrPerformButton } from "../ApproveOrPerformButton";
import ApproveButton from "../ApproveButton";
import PerformButton from "../PerformButton";
import { GeoWebParcel } from "./ParcelInfo";

dayjs.extend(utc);
Expand Down Expand Up @@ -76,6 +77,7 @@ function RejectBidAction(props: RejectBidActionProps) {
const [isActing, setIsActing] = React.useState(false);
const [displayNewForSalePrice, setDisplayNewForSalePrice] =
React.useState<string>(bidForSalePriceDisplay);
const [isAllowed, setIsAllowed] = React.useState(false);

const handleWrapModalOpen = () => setShowWrapModal(true);
const handleWrapModalClose = () => setShowWrapModal(false);
Expand Down Expand Up @@ -376,25 +378,32 @@ function RejectBidAction(props: RejectBidActionProps) {
className="w-100 mb-3"
onClick={handleWrapModalOpen}
>
{`Wrap to ${PAYMENT_TOKEN}`}
{`Wrap ETH to ${PAYMENT_TOKEN}`}
</Button>
<ApproveOrPerformButton
<ApproveButton
{...props}
isDisabled={isActing || isInvalid}
buttonText={"Reject Bid"}
isDisabled={isActing}
requiredFlowAmount={annualNetworkFeeRate ?? null}
requiredPayment={
penaltyPayment && newRequiredBuffer && oldRequiredBuffer
? penaltyPayment.add(newRequiredBuffer).sub(oldRequiredBuffer)
: null
}
performAction={rejectBid}
spender={licenseDiamondContract?.address ?? null}
requiredFlowPermissions={2}
flowOperator={licenseDiamondContract?.address ?? null}
setErrorMessage={setErrorMessage}
setIsActing={setIsActing}
setDidFail={setDidFail}
isAllowed={isAllowed}
setIsAllowed={setIsAllowed}
/>
<PerformButton
isDisabled={isActing || isInvalid}
isActing={isActing}
buttonText={"Reject Bid"}
performAction={rejectBid}
isAllowed={isAllowed}
/>
</Form>

Expand Down
2 changes: 1 addition & 1 deletion components/wrap/WrapModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ function WrapModal({
style={{ width: "128px" }}
disabled={isWrapping}
>
{isWrapping ? "Wrapping..." : `Wrap to ${PAYMENT_TOKEN}`}
{isWrapping ? "Wrapping..." : `Wrap ETH to ${PAYMENT_TOKEN}`}
</Button>
</form>
</Modal.Footer>
Expand Down
Empty file removed lib/wallets/connectors.js
Empty file.
1 change: 1 addition & 0 deletions public/task-done.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion tsconfig.tsbuildinfo

This file was deleted.

0 comments on commit aed464f

Please sign in to comment.