-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support SEP-8 "Action Required" flow (#164)
### What Support SEP-8 "Action Required" flow. Closes #113 ### Future Work The action fields received in the action required responses are all being handled as strings for now. I'll update this behavior to accept different kinds of inputs in the future, since the [SEP-9] field types can include "string", "country code", "phone number", date", "number" or "binary". [SEP-9]: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0009.md ### Screenshot https://user-images.githubusercontent.com/1952597/119165154-958bd600-ba33-11eb-9639-3d014c8003d4.mov ### How to test Use the jenkins preview link with `/account?secretKey=SBQZPYFQNQD4B7MP7PC3NKCMS5G5LD6PVO7BUYC34M4BBFN6KCXSKSVD` and send a small payment to `GBID36ML6VVNPIF6SQATQW5QIBREQAVEHQIUMWDLQCSVIYX2IJGK4KPP`. #### KYC Approval Criteria * Emails starting with "x" will get rejected * Emails not starting with "x" will get approved. If you need to repeat your tests you might need to delete the KYC already stored in the server with: ```curl curl -X DELETE https://sep8-server.dev.stellar.org/kyc-status/GASA5HCKMDAPLB6NIV7ADDA6YMTAQ2555TXKMS7FSVO44TJXHEGXLICZ ```
- Loading branch information
1 parent
0997f7c
commit 619dbcc
Showing
9 changed files
with
455 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { useEffect, useState } from "react"; | ||
import { useDispatch } from "react-redux"; | ||
import { Button, Input, Loader } from "@stellar/design-system"; | ||
import { Heading2 } from "components/Heading"; | ||
import { Modal } from "components/Modal"; | ||
import { | ||
initiateSep8SendAction, | ||
sep8SendActionRequiredFieldsAction, | ||
} from "ducks/sep8Send"; | ||
import { useRedux } from "hooks/useRedux"; | ||
import { | ||
ActionStatus, | ||
Sep8ActionRequiredResultType, | ||
Sep8Step, | ||
} from "types/types.d"; | ||
|
||
export const Sep8ActionRequiredForm = ({ | ||
onClose, | ||
}: { | ||
onClose: () => void; | ||
}) => { | ||
const { account, sep8Send } = useRedux("account", "sep8Send"); | ||
const [fieldValues, setFieldValues] = useState<{ [key: string]: string }>({}); | ||
const { | ||
actionFields, | ||
message, | ||
actionMethod, | ||
actionUrl, | ||
} = sep8Send.data.actionRequiredInfo; | ||
const { nextUrl, result } = sep8Send.data.actionRequiredResult; | ||
const dispatch = useDispatch(); | ||
|
||
useEffect(() => { | ||
if (sep8Send.data.sep8Step === Sep8Step.SENT_ACTION_REQUIRED_FIELDS) { | ||
if (result === Sep8ActionRequiredResultType.NO_FURTHER_ACTION_REQUIRED) { | ||
window.open(nextUrl, "_blank"); | ||
} | ||
|
||
if (account.data) { | ||
dispatch( | ||
initiateSep8SendAction({ | ||
assetCode: sep8Send.data.assetCode, | ||
assetIssuer: sep8Send.data.assetIssuer, | ||
homeDomain: sep8Send.data.homeDomain, | ||
}), | ||
); | ||
} | ||
} | ||
}, [ | ||
account.data, | ||
dispatch, | ||
nextUrl, | ||
result, | ||
sep8Send.data.assetCode, | ||
sep8Send.data.assetIssuer, | ||
sep8Send.data.homeDomain, | ||
sep8Send.data.sep8Step, | ||
]); | ||
|
||
const handleSubmitActionRequiredFields = () => { | ||
dispatch( | ||
sep8SendActionRequiredFieldsAction({ | ||
actionFields: fieldValues, | ||
actionMethod, | ||
actionUrl, | ||
}), | ||
); | ||
}; | ||
|
||
const handleOnChangeField = ({ | ||
fieldName, | ||
fieldValue, | ||
}: { | ||
fieldName: string; | ||
fieldValue: string; | ||
}) => { | ||
const buffFieldValue = { ...fieldValues }; | ||
buffFieldValue[fieldName] = fieldValue; | ||
setFieldValues(buffFieldValue); | ||
}; | ||
|
||
const renderSendPayment = () => ( | ||
<> | ||
<Heading2 className="ModalHeading">SEP-8 Action Required</Heading2> | ||
|
||
<div className="ModalBody"> | ||
<div className="ModalMessage"> | ||
<p>{message}</p> | ||
</div> | ||
|
||
<div className="ModalMessage"> | ||
<p>The following information is needed before we can proceed:</p> | ||
</div> | ||
|
||
{actionFields.map((fieldName) => ( | ||
<Input | ||
key={fieldName} | ||
id={`sep8-action-field-${fieldName}`} | ||
label={fieldName} | ||
onChange={(e) => | ||
handleOnChangeField({ fieldName, fieldValue: e.target.value }) | ||
} | ||
value={fieldValues[fieldName] || ""} | ||
/> | ||
))} | ||
|
||
{sep8Send.errorString && ( | ||
<div className="ModalMessage error"> | ||
<p>{sep8Send.errorString}</p> | ||
</div> | ||
)} | ||
</div> | ||
|
||
<div className="ModalButtonsFooter"> | ||
{sep8Send.status === ActionStatus.PENDING && <Loader />} | ||
|
||
<Button | ||
onClick={handleSubmitActionRequiredFields} | ||
disabled={sep8Send.status === ActionStatus.PENDING} | ||
> | ||
Submit | ||
</Button> | ||
</div> | ||
</> | ||
); | ||
|
||
return ( | ||
<Modal onClose={onClose} visible> | ||
{renderSendPayment()} | ||
</Modal> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,36 @@ | ||
import { useEffect, useState } from "react"; | ||
import { useDispatch } from "react-redux"; | ||
import { Sep8ActionRequiredForm } from "components/Sep8Send/Sep8ActionRequiredForm"; | ||
import { Sep8Approval } from "components/Sep8Send/Sep8Approval"; | ||
import { Sep8Review } from "components/Sep8Send/Sep8Review"; | ||
import { resetActiveAssetAction } from "ducks/activeAsset"; | ||
import { resetSep8SendAction } from "ducks/sep8Send"; | ||
import { useRedux } from "hooks/useRedux"; | ||
import { ActionStatus } from "types/types.d"; | ||
import { Sep8Step } from "types/types.d"; | ||
|
||
export const Sep8Send = () => { | ||
const { sep8Send } = useRedux("sep8Send"); | ||
const [approvalModalVisible, setApprovalModalVisible] = useState(false); | ||
const [reviewModalVisible, setReviewModalVisible] = useState(false); | ||
const sep8Step = sep8Send.data.sep8Step; | ||
const dispatch = useDispatch(); | ||
|
||
const onClose = () => { | ||
setApprovalModalVisible(false); | ||
dispatch(resetActiveAssetAction()); | ||
dispatch(resetSep8SendAction()); | ||
}; | ||
|
||
// use effect | ||
useEffect(() => { | ||
if (!sep8Send.status) { | ||
setReviewModalVisible(false); | ||
setApprovalModalVisible(false); | ||
return; | ||
} | ||
|
||
if (sep8Send.status !== ActionStatus.CAN_PROCEED) { | ||
return; | ||
} | ||
|
||
const hasTxToRevise = Boolean( | ||
sep8Send.data.revisedTransaction.revisedTxXdr, | ||
); | ||
setApprovalModalVisible(!hasTxToRevise); | ||
setReviewModalVisible(hasTxToRevise); | ||
}, [sep8Send.status, sep8Send.data.revisedTransaction.revisedTxXdr]); | ||
|
||
return ( | ||
<> | ||
{approvalModalVisible && <Sep8Approval onClose={onClose} />} | ||
{[Sep8Step.STARTING, Sep8Step.PENDING].includes(sep8Step) && ( | ||
<Sep8Approval onClose={onClose} /> | ||
)} | ||
|
||
{[Sep8Step.TRANSACTION_REVISED, Sep8Step.COMPLETE].includes(sep8Step) && ( | ||
<Sep8Review onClose={onClose} /> | ||
)} | ||
|
||
{reviewModalVisible && <Sep8Review onClose={onClose} />} | ||
{[ | ||
Sep8Step.ACTION_REQUIRED, | ||
Sep8Step.SENT_ACTION_REQUIRED_FIELDS, | ||
].includes(sep8Step) && <Sep8ActionRequiredForm onClose={onClose} />} | ||
</> | ||
); | ||
}; |
Oops, something went wrong.