diff --git a/package.json b/package.json index 4f812a247..5853f1833 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,8 @@ "clsx": "^2.1.0", "dayjs": "1.11.10", "ethers": "5.7.2", + "final-form": "^4.20.10", + "formik": "^2.4.5", "framer-motion": "^10.16.16", "gsap": "^3.12.4", "next": "14.0.4", diff --git a/src/components/BaseModal/index.tsx b/src/components/BaseModal/index.tsx index 48579b19a..1ee31dc11 100644 --- a/src/components/BaseModal/index.tsx +++ b/src/components/BaseModal/index.tsx @@ -21,6 +21,7 @@ const BaseModal = (props: PropsWithChildren): React.ReactNode = isShow, onHide, title, + className, children, description, headerClassName, @@ -34,7 +35,7 @@ const BaseModal = (props: PropsWithChildren): React.ReactNode = isCentered={true} > - + + + + { + step === MultiplierStep.authen && showManualCheck && ( + + Missing from the Leaderboard? + + ) + } + )} {step === MultiplierStep.signMessage && ( diff --git a/src/modules/Whitelist/steps/Step/styles.module.scss b/src/modules/Whitelist/steps/Step/styles.module.scss index a122593c8..984e94aed 100644 --- a/src/modules/Whitelist/steps/Step/styles.module.scss +++ b/src/modules/Whitelist/steps/Step/styles.module.scss @@ -57,3 +57,8 @@ border-radius: 0 !important; } } + +.modalContent { + border-radius: 0px !important; + min-width: unset !important; +} diff --git a/src/modules/Whitelist/steps/VerifyTwModal/index.tsx b/src/modules/Whitelist/steps/VerifyTwModal/index.tsx new file mode 100644 index 000000000..d15ba2916 --- /dev/null +++ b/src/modules/Whitelist/steps/VerifyTwModal/index.tsx @@ -0,0 +1,88 @@ +import { getError } from '@/utils/error'; +import { Button } from '@chakra-ui/react'; +import { FormikProps, useFormik } from 'formik'; +import React, { useState } from 'react'; +import s from './styles.module.scss'; +import { generateTokenWithTwPost } from '@/services/player-share'; +import { toast } from 'react-hot-toast'; +import { closeModal } from '@/stores/states/modal/reducer'; +import { useDispatch } from 'react-redux'; +import BaseModal from '@/components/BaseModal'; + +interface FormValues { + postUrl: string; +} + +export const ReferralModalID = 'ReferralModalID'; + +const VerifyTwModal = ({isShow, onHide, secretCode, onSuccess}: any) => { + const dispatch = useDispatch(); + const [isCreating, setIsCreating] = useState(false); + + const onSubmit = async (values: FormValues) => { + try { + setIsCreating(true); + const result = await generateTokenWithTwPost(secretCode as string, formValues.postUrl); + onSuccess && onSuccess(result); + dispatch(closeModal({ id: ReferralModalID })); + } catch (error) { + toast.error('Can not verify the post.'); + } finally { + setIsCreating(false); + } + }; + + const formik: FormikProps = useFormik({ + initialValues: { postUrl: '' } as FormValues, + onSubmit, + }); + + const formValues = React.useMemo(() => { + return formik.values; + }, [formik.values]); + + const onChangeText = (e: any) => { + formik.setValues((values: any) => ({ + ...values, + postUrl: e.target.value, + })); + }; + + return ( + +
+
+
+
Simply paste the URL of your tweet below to verify manually and we'll take care of the rest.
+
+ +
+ +
+
+
+
+ ); +}; + +export default VerifyTwModal; diff --git a/src/modules/Whitelist/steps/VerifyTwModal/styles.module.scss b/src/modules/Whitelist/steps/VerifyTwModal/styles.module.scss new file mode 100644 index 000000000..21e3eb75d --- /dev/null +++ b/src/modules/Whitelist/steps/VerifyTwModal/styles.module.scss @@ -0,0 +1,149 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; + + .content { + display: flex; + flex-direction: column; + align-items: center; + + width: 100%; + } + + .title { + text-align: center; + font-size: 24px; + font-weight: 700; + + margin-top: 8px; + } + + .desc { + //text-align: center; + font-size: 16px; + margin-bottom: 24px; + font-weight: 400; + color: #1C1C1C; + } + + .form { + width: 100%; + } + + .inputContainer { + border-radius: 0px; + background: rgba(255, 255, 255, 0.10); + border: 1px solid #CECECE; + display: flex; + padding: 12px; + align-items: center; + gap: 12px; + width: 100%; + margin-bottom: 24px; + } + + .input { + height: 100%; + overflow: hidden; + color: #000000; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 140%; + background-color: transparent; + width: 100%; + + &::placeholder { + color: #898989; + opacity: 1; /* Firefox */ + } + + &::-ms-input-placeholder { /* Edge 12-18 */ + color: #898989; + } + + &:focus { + outline: none; + } + } + + .button { + height: 48px !important; + padding: 8px 60px !important; + border-radius: 0px !important; + background: #FA4E0E !important; + color: #fff !important; + width: 180px !important; + } + +} + +.grid { + display: flex; + flex-direction: column; + align-items: center; + + // display: grid; + // grid-template-columns: 1fr 1fr 1fr; + gap: 20px; + + // @include w-max(768px) { + // grid-template-columns: 1fr 1fr; + // } + + // @include w-max(468px) { + // grid-template-columns: 1fr; + // } + + .item { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + padding: 28px 0px; + background-color: #1C1C1C; + padding: 28px 32px; + background-color: #1C1C1C; + min-width: 380px; + width: fit-content; + + @include w-max(768px) { + min-width: 320px; + } + } + + .item_title { + text-align: center; + font-size: 16px; + margin-top: 8px; + } + + .item_desc { + text-align: center; + font-size: 28px; + margin-top: 12px; + font-weight: 800; + line-height: 30px; + } + + .btn { + height: 16px; + opacity: 0.8; + cursor: pointer; + } +} + +.modalManualHeader { + padding-bottom: 0 !important; +} + +.modalContent { + :global { + .chakra-modal__body { + padding-left: rem(24) !important; + padding-right: rem(24) !important; + } + } +} + diff --git a/src/modules/Whitelist/steps/index.tsx b/src/modules/Whitelist/steps/index.tsx index 6b355fc12..ed6120667 100644 --- a/src/modules/Whitelist/steps/index.tsx +++ b/src/modules/Whitelist/steps/index.tsx @@ -16,13 +16,14 @@ import { commonSelector } from '@/stores/states/common/selector'; import { userSelector } from '@/stores/states/user/selector'; import copy from 'copy-to-clipboard'; import toast from 'react-hot-toast'; +import BaseModal from '@/components/BaseModal'; +import VerifyTwModal from '@/modules/Whitelist/steps/VerifyTwModal'; interface IAuthenCode { public_code: string; secret_code: string; } - const Steps = () => { const [authenCode, setAuthenCode] = useState(); const timer = useRef(); @@ -32,6 +33,7 @@ const Steps = () => { const { toggle: isShowConnect, onToggle: onToggleConnect } = useToggle(); const needReload = useAppSelector(commonSelector).needReload const user = useAppSelector(userSelector); + const [showManualCheck, setShowManualCheck] = useState(false); const handleShareTw = async () => { const res: any = await requestAuthenByShareCode(); @@ -85,21 +87,29 @@ const Steps = () => { const handleVerifyTwitter = async (): Promise => { try { const result = await generateTokenWithTwPost(authenCode?.secret_code as string); - if (result) { - clearInterval(timer.current); - const twitterToken = AuthenStorage.getAuthenKey(); - if (!twitterToken || twitterToken !== result?.token) { - AuthenStorage.setAuthenKey(result?.token); - setBearerToken(result?.token); - } - setSubmitting(false); - dispatch(requestReload()); - } + onVerifyTwSuccess(result); } catch (err) { console.log('handleVerifyTwitter', err); } }; + const onVerifyTwSuccess = (result: any) => { + if (result) { + clearInterval(timer.current); + const twitterToken = AuthenStorage.getAuthenKey(); + if (!twitterToken || twitterToken !== result?.token) { + AuthenStorage.setAuthenKey(result?.token); + setBearerToken(result?.token); + } + setSubmitting(false); + dispatch(requestReload()); + setShowManualCheck(false); + } + } + + const handleShowManualPopup = () => { + setShowManualCheck(true); + } const DATA_COMMUNITY = useMemo(() => { return ( @@ -116,7 +126,8 @@ const Steps = () => { right: { title: '+1 PTS', desc: 'per view' - } + }, + handleShowManualPopup: handleShowManualPopup, }, { title: 'Refer a friend to BVM', @@ -160,11 +171,27 @@ const Steps = () => { key={index} index={index} content={item} - isLoading={index === 0 && submitting} + isLoading={item.step === MultiplierStep.authen && submitting} /> ); })} + { + setShowManualCheck(false); + }} + secretCode={authenCode?.secret_code} + onSuccess={onVerifyTwSuccess} + /> + { + setShowManualCheck(false); + }} + secretCode={authenCode?.secret_code} + onSuccess={onVerifyTwSuccess} + />
); }; diff --git a/src/services/player-share.ts b/src/services/player-share.ts index 5239cf525..9d933df74 100644 --- a/src/services/player-share.ts +++ b/src/services/player-share.ts @@ -18,14 +18,16 @@ export const generateToken = async (uuid: string | string[]): Promise => { return; }; -export const generateTokenWithTwPost = async (uuid: string): Promise => { +export const generateTokenWithTwPost = async (uuid: string, link?: string): Promise => { try { const res = await apiClient.post(`/bvm/generate-token-with-twitter-post`, { secret_code: uuid, + link: link }); return Object(camelCaseKeys(res)); } catch (error) { - console.log(error); + throw error; + // console.log(error); } return; }; diff --git a/src/utils/format.ts b/src/utils/format.ts index 2cf74b3a3..a6b8b3532 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -160,3 +160,17 @@ export function formatString( export const zeroPad = (num: number, places: number) => String(num).padStart(places, '0'); + +export const formatMaxDecimals = (params: { value: any; maxDecimals?: number }) => { + const value = params.value; + const maxDecimals = params.maxDecimals !== undefined ? params.maxDecimals : 3; + + if ( + value && + value.toString().includes('.') && + value.toString().split('.')[1]?.length > maxDecimals + ) { + return undefined; + } + return value; +};