Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gating dApp #767

Merged
merged 22 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
78ccb1a
Add a feature flag `GATING_DAPP_ENABLED`
kkosiorowska Oct 16, 2024
246f78d
Move `HelperErrorText` to separate file
kkosiorowska Oct 16, 2024
1be9c17
Init `GateModal`
kkosiorowska Oct 16, 2024
4e8942b
Set `outline` as a default variant for input component
kkosiorowska Oct 16, 2024
e5fdf3e
Add password form to `GateModal`
kkosiorowska Oct 16, 2024
937feb9
Add a link to the Discord in `GateModal`
kkosiorowska Oct 21, 2024
1618ee8
Add a error message for password form
kkosiorowska Oct 21, 2024
9539b85
Make sure that input is controlled for password form
kkosiorowska Oct 21, 2024
39d4ba6
Open GateModal only for standalone dApp
kkosiorowska Oct 22, 2024
dd1a535
Add access code verification
kkosiorowska Oct 22, 2024
40c38df
Remove unneeded changes after rebase
kkosiorowska Oct 22, 2024
3fed9b4
Save encoded access code in `localStorage`
kkosiorowska Oct 22, 2024
5d76aeb
Separate logic into `useFormField` hook
kkosiorowska Oct 23, 2024
cc94dd7
Validate access code from the `localStorage`
kkosiorowska Oct 23, 2024
1da8272
Add `LoadingModal` to verify access code
kkosiorowska Oct 24, 2024
5e16b52
Merge branch 'main' of github.com:thesis/acre into gating-dapp-ui
kkosiorowska Oct 24, 2024
d0ced38
Handle errors for verify access code
kkosiorowska Oct 24, 2024
3edf103
Update styles for `LoadingModal`
kkosiorowska Oct 24, 2024
72e5bda
Return the boolean result instead of returning the data object
kkosiorowska Oct 24, 2024
26c28a8
Rename from `accessCode` to `encodedCode`
kkosiorowska Oct 24, 2024
60d9039
Add `TODO` comment
kkosiorowska Oct 24, 2024
9d9c120
Merge branch 'main' of github.com:thesis/acre into gating-dapp-ui
kkosiorowska Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dapp/.env
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ VITE_FEATURE_FLAG_XVERSE_WALLET_ENABLED="false"
VITE_FEATURE_FLAG_BEEHIVE_COMPONENT_ENABLED="false"
VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED="true"
VITE_FEATURE_FLAG_TVL_ENABLED="false"
VITE_FEATURE_GATING_DAPP_ENABLED="true"

34 changes: 34 additions & 0 deletions dapp/src/components/GateModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react"
import { Link, ModalBody, ModalFooter, ModalHeader } from "@chakra-ui/react"
import { TextSm } from "#/components/shared/Typography"
import { EXTERNAL_HREF } from "#/constants"
import { BaseModalProps } from "#/types"
import withBaseModal from "./ModalRoot/withBaseModal"
import PasswordForm from "./shared/PasswordForm"

export function GateModalBase({ closeModal }: BaseModalProps) {
return (
<>
<ModalHeader>Enter password</ModalHeader>
<ModalBody>
<PasswordForm submitButtonText="Connect" onSubmitForm={closeModal} />
</ModalBody>
<ModalFooter pt={0}>
<TextSm>
Don’t have a password? Contact us on{" "}
<Link
fontWeight="bold"
textDecoration="underline"
href={EXTERNAL_HREF.DISCORD}
isExternal
>
Discord
</Link>
</TextSm>
</ModalFooter>
</>
)
}

const GateModal = withBaseModal(GateModalBase)
export default GateModal
2 changes: 2 additions & 0 deletions dapp/src/components/ModalRoot/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import MezoBeehiveModal from "../MezoBeehiveModal"
import ConnectWalletModal from "../ConnectWalletModal"
import UnexpectedErrorModal from "../UnexpectedErrorModal"
import AcrePointsClaimModal from "../AcrePointsClaimModal"
import GateModal from "../GateModal"

const MODALS: Record<ModalType, ElementType> = {
STAKE: TransactionModal,
Expand All @@ -16,6 +17,7 @@ const MODALS: Record<ModalType, ElementType> = {
CONNECT_WALLET: ConnectWalletModal,
UNEXPECTED_ERROR: UnexpectedErrorModal,
ACRE_POINTS_CLAIM: AcrePointsClaimModal,
GATE: GateModal,
} as const

export default function ModalRoot() {
Expand Down
52 changes: 52 additions & 0 deletions dapp/src/components/shared/Form/FormInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { ChangeEvent, useCallback } from "react"
import { FormControl, FormLabel, Input, InputProps } from "@chakra-ui/react"
import { useField } from "formik"
import { logPromiseFailure } from "#/utils"
import HelperErrorText from "./HelperErrorText"

export type FormInputProps = {
name: string
label?: string
helperText?: string | JSX.Element
} & Omit<InputProps, "id" | "isInvalid" | "value" | "onChange">

export default function FormInput({
name,
label,
helperText,
...inputProps
}: FormInputProps) {
const [field, meta, helpers] = useField<string>(name)

const hasError = Boolean(meta.error)
const errorMsgText = meta.error

const handleChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
if (!meta.touched) logPromiseFailure(helpers.setTouched(true))
if (meta.error) helpers.setError(undefined)

logPromiseFailure(helpers.setValue(event.target.value))
},
[helpers, meta.touched, meta.error],
)
r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved

return (
<FormControl isInvalid={hasError} isDisabled={inputProps.isDisabled}>
{label && <FormLabel htmlFor={name}>{label}</FormLabel>}
<Input
{...inputProps}
{...field}
id={name}
isInvalid={hasError}
value={meta.value}
onChange={handleChange}
/>
<HelperErrorText
helperText={helperText}
errorMsgText={errorMsgText}
hasError={hasError}
/>
</FormControl>
)
}
40 changes: 40 additions & 0 deletions dapp/src/components/shared/Form/HelperErrorText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react"
import { FormErrorMessage, FormHelperText, Icon } from "@chakra-ui/react"
import { IconInfoCircle } from "@tabler/icons-react"
import { Alert, AlertIcon, AlertDescription } from "../Alert"

export type HelperErrorTextProps = {
errorMsgText?: string | JSX.Element
hasError?: boolean
helperText?: string | JSX.Element
}

export default function HelperErrorText({
helperText,
errorMsgText,
hasError,
}: HelperErrorTextProps) {
if (hasError) {
return (
<FormErrorMessage>
<Alert status="error">
<AlertIcon status="error" />
<AlertDescription>
{errorMsgText || "Please enter a valid value"}
</AlertDescription>
</Alert>
</FormErrorMessage>
)
}

if (helperText) {
return (
<FormHelperText>
<Icon as={IconInfoCircle} />
{helperText}
</FormHelperText>
)
}

return null
}
30 changes: 30 additions & 0 deletions dapp/src/components/shared/PasswordForm/PasswordFormBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react"
import { FormikProps } from "formik"
import { Form, FormSubmitButton } from "../Form"
import FormInput from "../Form/FormInput"

const PASSWORD_FIELD_NAME = "password"

export type PasswordFormValues = {
[PASSWORD_FIELD_NAME]?: string
}

export type PasswordFormBaseProps = {
formId?: string
label?: string
submitButtonText: string
}

export default function PasswordFormBase({
formId,
label,
submitButtonText,
...formikProps
}: PasswordFormBaseProps & FormikProps<PasswordFormValues>) {
return (
<Form id={formId} onSubmit={formikProps.handleSubmit} w="100%">
<FormInput name={PASSWORD_FIELD_NAME} label={label} type="password" />
<FormSubmitButton mt={10}>{submitButtonText}</FormSubmitButton>
</Form>
)
}
30 changes: 30 additions & 0 deletions dapp/src/components/shared/PasswordForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FormikErrors, withFormik } from "formik"
import { BaseFormProps } from "#/types"
import { getErrorsObj, validatePassword } from "#/utils"
import PasswordFormBase, {
PasswordFormBaseProps,
PasswordFormValues,
} from "./PasswordFormBase"

type PasswordFormProps = PasswordFormBaseProps &
BaseFormProps<PasswordFormValues>

const PasswordForm = withFormik<PasswordFormProps, PasswordFormValues>({
mapPropsToValues: () => ({
password: "",
}),
validate: async ({ password }) => {
const errors: FormikErrors<PasswordFormValues> = {}

errors.password = await validatePassword(password)

return getErrorsObj(errors)
},
handleSubmit: (values, { props }) => {
props.onSubmitForm(values)
},
validateOnBlur: false,
validateOnChange: false,
})(PasswordFormBase)

export default PasswordForm
47 changes: 3 additions & 44 deletions dapp/src/components/shared/TokenBalanceInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import {
Box,
Button,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Icon,
InputGroup,
InputProps,
InputRightElement,
Expand All @@ -19,52 +17,13 @@ import {
userAmountToBigInt,
} from "#/utils"
import { CurrencyType } from "#/types"
import { IconInfoCircle } from "@tabler/icons-react"
import { useCurrencyConversion } from "#/hooks"
import NumberFormatInput, {
NumberFormatInputValues,
NumberFormatInputProps,
} from "../NumberFormatInput"
import { CurrencyBalance } from "../CurrencyBalance"
import { Alert, AlertIcon, AlertDescription } from "../Alert"

const VARIANT = "balance"

type HelperErrorTextProps = {
errorMsgText?: string | JSX.Element
hasError?: boolean
helperText?: string | JSX.Element
}

function HelperErrorText({
helperText,
errorMsgText,
hasError,
}: HelperErrorTextProps) {
if (hasError) {
return (
<FormErrorMessage>
<Alert status="error">
<AlertIcon status="error" />
<AlertDescription>
{errorMsgText || "Please enter a valid value"}
</AlertDescription>
</Alert>
</FormErrorMessage>
)
}

if (helperText) {
return (
<FormHelperText>
<Icon as={IconInfoCircle} />
{helperText}
</FormHelperText>
)
}

return null
}
import HelperErrorText, { HelperErrorTextProps } from "../Form/HelperErrorText"

type FiatCurrencyBalanceProps = {
amount: bigint
Expand Down Expand Up @@ -173,10 +132,10 @@ export default function TokenBalanceInput({
</Box>
</Box>
</FormLabel>
<InputGroup variant={VARIANT}>
<InputGroup>
<NumberFormatInput
variant="outline"
size={size}
variant={VARIANT}
isInvalid={hasError}
suffix={` ${symbol}`}
placeholder={placeholder}
Expand Down
5 changes: 5 additions & 0 deletions dapp/src/constants/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export const TOKEN_FORM_ERRORS = {
`The amount is below the minimum required deposit of ${minValue} BTC.`,
}

export const PASSWORD_FORM_ERRORS = {
REQUIRED: "Please enter a password.",
INCORRECT_VALUE: "Incorrect password. Please try again.",
}

export const ACTION_FORM_ERRORS = {
[ACTION_FLOW_TYPES.STAKE]: TOKEN_FORM_ERRORS,
[ACTION_FLOW_TYPES.UNSTAKE]: {
Expand Down
4 changes: 4 additions & 0 deletions dapp/src/constants/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const ACRE_POINTS_ENABLED =

const TVL_ENABLED = import.meta.env.VITE_FEATURE_FLAG_TVL_ENABLED === "true"

const GATING_DAPP_ENABLED =
import.meta.env.VITE_FEATURE_GATING_DAPP_ENABLED === "true"

const featureFlags = {
GAMIFICATION_ENABLED,
OKX_WALLET_ENABLED,
Expand All @@ -26,6 +29,7 @@ const featureFlags = {
BEEHIVE_COMPONENT_ENABLED,
ACRE_POINTS_ENABLED,
TVL_ENABLED,
GATING_DAPP_ENABLED,
}

export default featureFlags
19 changes: 19 additions & 0 deletions dapp/src/hooks/useGatingDApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect, useRef } from "react"
import { MODAL_TYPES } from "#/types"
import { featureFlags } from "#/constants"
import { useModal } from "./useModal"

export default function useGatingDApp() {
const { openModal } = useModal()
const isMounted = useRef(false)

useEffect(() => {
if (featureFlags.GATING_DAPP_ENABLED && !isMounted.current) {
isMounted.current = true
openModal(MODAL_TYPES.GATE, {
withCloseButton: false,
closeOnEsc: false,
})
}
r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved
}, [openModal])
}
2 changes: 2 additions & 0 deletions dapp/src/hooks/useInitApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { useSentry } from "./sentry"
import useDetectReferral from "./useDetectReferral"
import { useDisconnectWallet } from "./useDisconnectWallet"
import { useFetchBTCPriceUSD } from "./useFetchBTCPriceUSD"
import useGatingDApp from "./useGatingDApp"

export function useInitApp() {
// TODO: Let's uncomment when dark mode is ready
// useDetectThemeMode()
useSentry()
useDetectReferral()
useGatingDApp()
useInitializeAcreSdk()
useInitDataFromSdk()
useFetchBTCPriceUSD()
Expand Down
Loading
Loading