From ebf3fbf67d941b00968f6efb53adb98c63d940a0 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 30 Jul 2024 15:59:33 +0200 Subject: [PATCH 01/22] Create a custom impl of `useLocalStorage` hook --- dapp/package.json | 1 + dapp/src/hooks/index.ts | 1 + dapp/src/hooks/useLocalStorage.ts | 5 +++++ pnpm-lock.yaml | 12 ++++++++++++ 4 files changed, 19 insertions(+) create mode 100644 dapp/src/hooks/useLocalStorage.ts diff --git a/dapp/package.json b/dapp/package.json index 364661f45..680fc6447 100644 --- a/dapp/package.json +++ b/dapp/package.json @@ -23,6 +23,7 @@ "@orangekit/react": "1.0.0-beta.28", "@orangekit/sign-in-with-wallet": "1.0.0-beta.6", "@reduxjs/toolkit": "^2.2.0", + "@rehooks/local-storage": "^2.4.5", "@safe-global/safe-core-sdk-types": "^5.0.1", "@sentry/react": "^7.98.0", "@sentry/types": "^7.102.0", diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index 6a217f4f5..c43856176 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -29,3 +29,4 @@ export { default as useTriggerConnectWalletModal } from "./useTriggerConnectWall export { default as useMobileMode } from "./useMobileMode" export { default as useBitcoinRecoveryAddress } from "./useBitcoinRecoveryAddress" export { default as useIsFetchedWalletData } from "./useIsFetchedWalletData" +export { default as useLocalStorage } from "./useLocalStorage" diff --git a/dapp/src/hooks/useLocalStorage.ts b/dapp/src/hooks/useLocalStorage.ts new file mode 100644 index 000000000..4eed2d95f --- /dev/null +++ b/dapp/src/hooks/useLocalStorage.ts @@ -0,0 +1,5 @@ +import { useLocalStorage as useRehooksLocalStorage } from "@rehooks/local-storage" + +export default function useLocalStorage(key: string, defaultValue: T) { + return useRehooksLocalStorage(key, defaultValue) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1daa23e0..3d6ca719f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: '@reduxjs/toolkit': specifier: ^2.2.0 version: 2.2.5(react-redux@9.1.2)(react@18.3.1) + '@rehooks/local-storage': + specifier: ^2.4.5 + version: 2.4.5(react@18.3.1) '@safe-global/safe-core-sdk-types': specifier: ^5.0.1 version: 5.0.1(typescript@5.4.5) @@ -6503,6 +6506,15 @@ packages: reselect: 5.1.1 dev: false + /@rehooks/local-storage@2.4.5(react@18.3.1): + resolution: {integrity: sha512-3Q4KtiUBaKoIDRK72BWfAy50ul6hbw29f/M7tyCzlMe2FbSsiQNok0WGeBLaYj4T2PJ7JMSJlSbUGY8RNsImmw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.3.1 + dev: false + /@remix-run/router@1.16.1: resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} engines: {node: '>=14.0.0'} From 1a2572ed9864212fe3f629ea2833b2065263069a Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 30 Jul 2024 17:05:36 +0200 Subject: [PATCH 02/22] Detect the referral through the URL parameter DApp should detect by which partner it was opened. As a result, we will be able to customize the solutions depending on the partner. --- dapp/src/contexts/StakeFlowContext.tsx | 6 ++-- dapp/src/hooks/index.ts | 2 ++ dapp/src/hooks/useDetectReferral.ts | 10 ++++++ dapp/src/hooks/useInitApp.ts | 2 ++ dapp/src/hooks/useReferral.ts | 45 ++++++++++++++++++++++++++ dapp/src/types/index.ts | 1 + dapp/src/types/referral.ts | 5 +++ 7 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 dapp/src/hooks/useDetectReferral.ts create mode 100644 dapp/src/hooks/useReferral.ts create mode 100644 dapp/src/types/referral.ts diff --git a/dapp/src/contexts/StakeFlowContext.tsx b/dapp/src/contexts/StakeFlowContext.tsx index 44225c338..d3eb6b05f 100644 --- a/dapp/src/contexts/StakeFlowContext.tsx +++ b/dapp/src/contexts/StakeFlowContext.tsx @@ -6,6 +6,7 @@ import { } from "#/acre-react/hooks" import { env } from "#/constants" import useBitcoinRecoveryAddress from "#/hooks/useBitcoinRecoveryAddress" +import useReferral from "#/hooks/useReferral" type StakeFlowContextValue = Omit & { initStake: () => Promise @@ -25,12 +26,13 @@ export function StakeFlowProvider({ children }: { children: React.ReactNode }) { stake, } = useStakeFlow() const bitcoinRecoveryAddress = useBitcoinRecoveryAddress() + const { referral } = useReferral() const initStake = useCallback(async () => { if (!acre) throw new Error("Acre SDK not defined") - await acreInitStake(env.REFERRAL, bitcoinRecoveryAddress) - }, [acre, acreInitStake, bitcoinRecoveryAddress]) + await acreInitStake(referral ?? env.REFERRAL, bitcoinRecoveryAddress) + }, [acre, acreInitStake, bitcoinRecoveryAddress, referral]) const context = useMemo( () => ({ diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index c43856176..e879973be 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -30,3 +30,5 @@ export { default as useMobileMode } from "./useMobileMode" export { default as useBitcoinRecoveryAddress } from "./useBitcoinRecoveryAddress" export { default as useIsFetchedWalletData } from "./useIsFetchedWalletData" export { default as useLocalStorage } from "./useLocalStorage" +export { default as useDetectReferral } from "./useDetectReferral" +export { default as useReferral } from "./useReferral" diff --git a/dapp/src/hooks/useDetectReferral.ts b/dapp/src/hooks/useDetectReferral.ts new file mode 100644 index 000000000..e32a457b6 --- /dev/null +++ b/dapp/src/hooks/useDetectReferral.ts @@ -0,0 +1,10 @@ +import { useEffect } from "react" +import useReferral from "./useReferral" + +export default function useDetectReferral() { + const { detectReferral } = useReferral() + + useEffect(() => { + detectReferral() + }, [detectReferral]) +} diff --git a/dapp/src/hooks/useInitApp.ts b/dapp/src/hooks/useInitApp.ts index 0f4390a66..42c21151f 100644 --- a/dapp/src/hooks/useInitApp.ts +++ b/dapp/src/hooks/useInitApp.ts @@ -3,6 +3,7 @@ import { useAccountsChangedUnisat } from "./orangeKit/useAccountsChangedUnisat" import { useDisconnectOKX } from "./orangeKit/useDisconnectOKX" import { useInitDataFromSdk, useInitializeAcreSdk } from "./sdk" import { useSentry } from "./sentry" +import useDetectReferral from "./useDetectReferral" import { useDisconnectWallet } from "./useDisconnectWallet" import { useFetchBTCPriceUSD } from "./useFetchBTCPriceUSD" @@ -10,6 +11,7 @@ export function useInitApp() { // TODO: Let's uncomment when dark mode is ready // useDetectThemeMode() useSentry() + useDetectReferral() useInitializeAcreSdk() useInitDataFromSdk() useFetchBTCPriceUSD() diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts new file mode 100644 index 000000000..e7079a00e --- /dev/null +++ b/dapp/src/hooks/useReferral.ts @@ -0,0 +1,45 @@ +import { Referral, REFERRALS } from "#/types" +import { useMemo, useCallback } from "react" +import useLocalStorage from "./useLocalStorage" + +const PARAM_NAME = "ref" + +function isOfTypeReferral(keyInput: number): keyInput is Referral { + const referral = Object.values(REFERRALS) as number[] + return referral.includes(keyInput) +} + +type UseReferralReturn = { + referral?: Referral + detectReferral: () => void + resetReferral: () => void +} + +export default function useReferral(): UseReferralReturn { + const [referral, setReferral] = useLocalStorage( + "referral", + undefined, + ) + + const detectReferral = useCallback(() => { + const params = new URLSearchParams(window.location.search) + const detectedReferral = params.get(PARAM_NAME) + + if (detectedReferral && isOfTypeReferral(parseInt(detectedReferral, 10))) { + setReferral(detectedReferral) + } + }, [setReferral]) + + const resetReferral = useCallback(() => { + setReferral(undefined) + }, [setReferral]) + + return useMemo( + () => ({ + detectReferral, + resetReferral, + referral, + }), + [detectReferral, resetReferral, referral], + ) +} diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index ec4b743ac..04bea679d 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -15,3 +15,4 @@ export * from "./eip1193" export * from "./error" export * from "./status" export * from "./orangekit" +export * from "./referral" diff --git a/dapp/src/types/referral.ts b/dapp/src/types/referral.ts new file mode 100644 index 000000000..2e9c1ebf6 --- /dev/null +++ b/dapp/src/types/referral.ts @@ -0,0 +1,5 @@ +export const REFERRALS = { + STAKING_REWARDS: 48450, +} as const + +export type Referral = (typeof REFERRALS)[keyof typeof REFERRALS] From d6a9fc33aa304abc84f08922cf002dd77bf28862 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 30 Jul 2024 17:49:03 +0200 Subject: [PATCH 03/22] Fix failed dApp build --- dapp/src/hooks/useReferral.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index e7079a00e..3a61188bd 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -23,9 +23,10 @@ export default function useReferral(): UseReferralReturn { const detectReferral = useCallback(() => { const params = new URLSearchParams(window.location.search) - const detectedReferral = params.get(PARAM_NAME) + const param = params.get(PARAM_NAME) + const detectedReferral = param ? parseInt(param, 10) : null - if (detectedReferral && isOfTypeReferral(parseInt(detectedReferral, 10))) { + if (detectedReferral && isOfTypeReferral(detectedReferral)) { setReferral(detectedReferral) } }, [setReferral]) From 4b2256f1e7900e288291af27b43c6f09f7a8ffaa Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 31 Jul 2024 09:46:10 +0200 Subject: [PATCH 04/22] Set the default referral to 0 --- dapp/.env | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dapp/.env b/dapp/.env index 8e23a13a7..8078c2a97 100644 --- a/dapp/.env +++ b/dapp/.env @@ -7,8 +7,7 @@ VITE_SENTRY_DSN="" # TODO: Use a more general source VITE_ETH_HOSTNAME_HTTP="https://sepolia.infura.io/v3/c80e8ccdcc4c4a809bce4fc165310617" -# TODO: Set the correct referral -VITE_REFERRAL=123 +VITE_REFERRAL=0 # TODO: Set this env variable in CI. VITE_TBTC_API_ENDPOINT="" From b94b0a6cb27a50137c28ea77cb90aa2e4eb9b5dd Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 31 Jul 2024 10:02:17 +0200 Subject: [PATCH 05/22] Support the default value for the referral --- dapp/src/constants/index.ts | 1 + dapp/src/constants/referrals.ts | 6 ++++++ dapp/src/contexts/StakeFlowContext.tsx | 3 +-- dapp/src/hooks/useReferral.ts | 22 ++++++++++++---------- dapp/src/types/index.ts | 1 - dapp/src/types/referral.ts | 5 ----- 6 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 dapp/src/constants/referrals.ts delete mode 100644 dapp/src/types/referral.ts diff --git a/dapp/src/constants/index.ts b/dapp/src/constants/index.ts index 5ab47427b..6e46d0a36 100644 --- a/dapp/src/constants/index.ts +++ b/dapp/src/constants/index.ts @@ -12,3 +12,4 @@ export * from "./staking" export { default as tbtc } from "./tbtc" export * from "./time" export { default as wallets } from "./wallets" +export { default as referrals } from "./referrals" diff --git a/dapp/src/constants/referrals.ts b/dapp/src/constants/referrals.ts new file mode 100644 index 000000000..f142d9927 --- /dev/null +++ b/dapp/src/constants/referrals.ts @@ -0,0 +1,6 @@ +import env from "#/constants/env" + +export default { + DEFAULT: env.REFERRAL, + STAKING_REWARDS: 48450, +} diff --git a/dapp/src/contexts/StakeFlowContext.tsx b/dapp/src/contexts/StakeFlowContext.tsx index d3eb6b05f..a674d0526 100644 --- a/dapp/src/contexts/StakeFlowContext.tsx +++ b/dapp/src/contexts/StakeFlowContext.tsx @@ -4,7 +4,6 @@ import { useAcreContext, useStakeFlow, } from "#/acre-react/hooks" -import { env } from "#/constants" import useBitcoinRecoveryAddress from "#/hooks/useBitcoinRecoveryAddress" import useReferral from "#/hooks/useReferral" @@ -31,7 +30,7 @@ export function StakeFlowProvider({ children }: { children: React.ReactNode }) { const initStake = useCallback(async () => { if (!acre) throw new Error("Acre SDK not defined") - await acreInitStake(referral ?? env.REFERRAL, bitcoinRecoveryAddress) + await acreInitStake(referral, bitcoinRecoveryAddress) }, [acre, acreInitStake, bitcoinRecoveryAddress, referral]) const context = useMemo( diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index 3a61188bd..d16a44438 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -1,24 +1,24 @@ -import { Referral, REFERRALS } from "#/types" -import { useMemo, useCallback } from "react" +import { referrals } from "#/constants" +import { useCallback, useMemo } from "react" import useLocalStorage from "./useLocalStorage" const PARAM_NAME = "ref" -function isOfTypeReferral(keyInput: number): keyInput is Referral { - const referral = Object.values(REFERRALS) as number[] - return referral.includes(keyInput) +function isExistingReferral(detectedReferral: number) { + const referral = Object.values(referrals) + return referral.includes(detectedReferral) } type UseReferralReturn = { - referral?: Referral + referral: number detectReferral: () => void resetReferral: () => void } export default function useReferral(): UseReferralReturn { - const [referral, setReferral] = useLocalStorage( + const [referral, setReferral] = useLocalStorage( "referral", - undefined, + referrals.DEFAULT, ) const detectReferral = useCallback(() => { @@ -26,13 +26,15 @@ export default function useReferral(): UseReferralReturn { const param = params.get(PARAM_NAME) const detectedReferral = param ? parseInt(param, 10) : null - if (detectedReferral && isOfTypeReferral(detectedReferral)) { + if (detectedReferral && isExistingReferral(detectedReferral)) { setReferral(detectedReferral) + } else { + setReferral(referrals.DEFAULT) } }, [setReferral]) const resetReferral = useCallback(() => { - setReferral(undefined) + setReferral(referrals.DEFAULT) }, [setReferral]) return useMemo( diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index 04bea679d..ec4b743ac 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -15,4 +15,3 @@ export * from "./eip1193" export * from "./error" export * from "./status" export * from "./orangekit" -export * from "./referral" diff --git a/dapp/src/types/referral.ts b/dapp/src/types/referral.ts deleted file mode 100644 index 2e9c1ebf6..000000000 --- a/dapp/src/types/referral.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const REFERRALS = { - STAKING_REWARDS: 48450, -} as const - -export type Referral = (typeof REFERRALS)[keyof typeof REFERRALS] From 216583f4cfddb989c9a0cfc69277d488e3def969 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Mon, 5 Aug 2024 08:53:35 +0200 Subject: [PATCH 06/22] Allow setting of unregistered referral --- dapp/src/constants/index.ts | 1 - dapp/src/constants/referrals.ts | 6 ------ dapp/src/hooks/useReferral.ts | 15 +++++---------- 3 files changed, 5 insertions(+), 17 deletions(-) delete mode 100644 dapp/src/constants/referrals.ts diff --git a/dapp/src/constants/index.ts b/dapp/src/constants/index.ts index 6e46d0a36..5ab47427b 100644 --- a/dapp/src/constants/index.ts +++ b/dapp/src/constants/index.ts @@ -12,4 +12,3 @@ export * from "./staking" export { default as tbtc } from "./tbtc" export * from "./time" export { default as wallets } from "./wallets" -export { default as referrals } from "./referrals" diff --git a/dapp/src/constants/referrals.ts b/dapp/src/constants/referrals.ts deleted file mode 100644 index f142d9927..000000000 --- a/dapp/src/constants/referrals.ts +++ /dev/null @@ -1,6 +0,0 @@ -import env from "#/constants/env" - -export default { - DEFAULT: env.REFERRAL, - STAKING_REWARDS: 48450, -} diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index d16a44438..629010c30 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -1,14 +1,9 @@ -import { referrals } from "#/constants" +import { env } from "#/constants" import { useCallback, useMemo } from "react" import useLocalStorage from "./useLocalStorage" const PARAM_NAME = "ref" -function isExistingReferral(detectedReferral: number) { - const referral = Object.values(referrals) - return referral.includes(detectedReferral) -} - type UseReferralReturn = { referral: number detectReferral: () => void @@ -18,7 +13,7 @@ type UseReferralReturn = { export default function useReferral(): UseReferralReturn { const [referral, setReferral] = useLocalStorage( "referral", - referrals.DEFAULT, + env.REFERRAL, ) const detectReferral = useCallback(() => { @@ -26,15 +21,15 @@ export default function useReferral(): UseReferralReturn { const param = params.get(PARAM_NAME) const detectedReferral = param ? parseInt(param, 10) : null - if (detectedReferral && isExistingReferral(detectedReferral)) { + if (detectedReferral) { setReferral(detectedReferral) } else { - setReferral(referrals.DEFAULT) + setReferral(env.REFERRAL) } }, [setReferral]) const resetReferral = useCallback(() => { - setReferral(referrals.DEFAULT) + setReferral(env.REFERRAL) }, [setReferral]) return useMemo( From 33e8ba4d87c882567514fc6bea46636b5b29ce9e Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Mon, 5 Aug 2024 10:55:41 +0200 Subject: [PATCH 07/22] Use search referral param for all router path --- .../components/Header/Navigation/NavigationItem.tsx | 13 ++++++++++++- dapp/src/hooks/useReferral.ts | 5 ++--- dapp/src/router/path.ts | 4 ++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/dapp/src/components/Header/Navigation/NavigationItem.tsx b/dapp/src/components/Header/Navigation/NavigationItem.tsx index ad5c4817f..9dd6806f0 100644 --- a/dapp/src/components/Header/Navigation/NavigationItem.tsx +++ b/dapp/src/components/Header/Navigation/NavigationItem.tsx @@ -7,6 +7,8 @@ import { } from "@chakra-ui/react" import { motion } from "framer-motion" import { NavigationItemType } from "#/types/navigation" +import { To, useSearchParams } from "react-router-dom" +import { SEARCH_PARAMS_NAMES } from "#/router/path" import { NavLink } from "../../shared/NavLink" type NavigationItemProps = ListItemProps & NavigationItemType @@ -14,10 +16,19 @@ type NavigationItemProps = ListItemProps & NavigationItemType function NavigationItem(props: NavigationItemProps) { const { label, href, ...restProps } = props const styles = useMultiStyleConfig("Link", { variant: "navigation" }) + const [searchParams] = useSearchParams() + + const referralParam = searchParams.get(SEARCH_PARAMS_NAMES.referral) + const to: To = { + pathname: href, + ...(referralParam && { + search: `?${SEARCH_PARAMS_NAMES.referral}=${referralParam}`, + }), + } return ( - + {({ isActive }) => ( <> {label} diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index 629010c30..a8563a0e3 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -1,9 +1,8 @@ import { env } from "#/constants" import { useCallback, useMemo } from "react" +import { SEARCH_PARAMS_NAMES } from "#/router/path" import useLocalStorage from "./useLocalStorage" -const PARAM_NAME = "ref" - type UseReferralReturn = { referral: number detectReferral: () => void @@ -18,7 +17,7 @@ export default function useReferral(): UseReferralReturn { const detectReferral = useCallback(() => { const params = new URLSearchParams(window.location.search) - const param = params.get(PARAM_NAME) + const param = params.get(SEARCH_PARAMS_NAMES.referral) const detectedReferral = param ? parseInt(param, 10) : null if (detectedReferral) { diff --git a/dapp/src/router/path.ts b/dapp/src/router/path.ts index 2fc1bdf23..dc8bf6e26 100644 --- a/dapp/src/router/path.ts +++ b/dapp/src/router/path.ts @@ -1,3 +1,7 @@ +export const SEARCH_PARAMS_NAMES = { + referral: "ref", +} + export const routerPath = { home: "/", dashboard: "/dashboard", From bc3c8e7d7ea84f2db972bc1edf87a832cfba49ad Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Mon, 5 Aug 2024 11:34:54 +0200 Subject: [PATCH 08/22] Referral should be a number only up to max `uint16` --- dapp/src/hooks/useReferral.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index a8563a0e3..19f99bc45 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -3,6 +3,8 @@ import { useCallback, useMemo } from "react" import { SEARCH_PARAMS_NAMES } from "#/router/path" import useLocalStorage from "./useLocalStorage" +const MAX_UINT16 = 65535 + type UseReferralReturn = { referral: number detectReferral: () => void @@ -20,7 +22,7 @@ export default function useReferral(): UseReferralReturn { const param = params.get(SEARCH_PARAMS_NAMES.referral) const detectedReferral = param ? parseInt(param, 10) : null - if (detectedReferral) { + if (detectedReferral && detectedReferral <= MAX_UINT16) { setReferral(detectedReferral) } else { setReferral(env.REFERRAL) From b0c62ea2a408b01162b03ff8de09dee71c1dd223 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Mon, 5 Aug 2024 11:48:18 +0200 Subject: [PATCH 09/22] Check if referral is greater than or equal to zero --- dapp/src/hooks/useReferral.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index 19f99bc45..a2a325082 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -22,7 +22,11 @@ export default function useReferral(): UseReferralReturn { const param = params.get(SEARCH_PARAMS_NAMES.referral) const detectedReferral = param ? parseInt(param, 10) : null - if (detectedReferral && detectedReferral <= MAX_UINT16) { + if ( + detectedReferral && + detectedReferral >= 0 && + detectedReferral <= MAX_UINT16 + ) { setReferral(detectedReferral) } else { setReferral(env.REFERRAL) From e09acd9d9beb25b03a3e0e48fb731814a6258571 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 11:48:57 +0200 Subject: [PATCH 10/22] Separate `UnexpectedErrorModal` into a dedicated component --- dapp/src/components/ConnectWalletModal/index.tsx | 1 - dapp/src/components/ModalRoot/index.tsx | 2 ++ .../ActiveStakingStep/StakingErrorModal/index.tsx | 11 ++++++++--- .../components/TransactionModal/ErrorModal.tsx | 15 +++++++++++---- .../TransactionModal/ModalContentWrapper.tsx | 5 ++++- .../UnexpectedErrorModal.tsx | 11 +++++++++-- dapp/src/types/modal.ts | 2 ++ 7 files changed, 36 insertions(+), 11 deletions(-) rename dapp/src/components/{TransactionModal => }/UnexpectedErrorModal.tsx (72%) diff --git a/dapp/src/components/ConnectWalletModal/index.tsx b/dapp/src/components/ConnectWalletModal/index.tsx index 0957a9a5e..bac59c3d6 100644 --- a/dapp/src/components/ConnectWalletModal/index.tsx +++ b/dapp/src/components/ConnectWalletModal/index.tsx @@ -12,7 +12,6 @@ export function ConnectWalletModalBase({ withCloseButton = true, }: { onSuccess?: OnSuccessCallback - withCloseButton?: boolean } & BaseModalProps) { const connectors = useConnectors() const enabledConnectors = connectors.map((connector) => ({ diff --git a/dapp/src/components/ModalRoot/index.tsx b/dapp/src/components/ModalRoot/index.tsx index c7567889a..7ca8db6e0 100644 --- a/dapp/src/components/ModalRoot/index.tsx +++ b/dapp/src/components/ModalRoot/index.tsx @@ -5,6 +5,7 @@ import TransactionModal from "../TransactionModal" import WelcomeModal from "../WelcomeModal" import MezoBeehiveModal from "../MezoBeehiveModal" import ConnectWalletModal from "../ConnectWalletModal" +import UnexpectedErrorModal from "../UnexpectedErrorModal" const MODALS: Record = { STAKE: TransactionModal, @@ -12,6 +13,7 @@ const MODALS: Record = { WELCOME: WelcomeModal, MEZO_BEEHIVE: MezoBeehiveModal, CONNECT_WALLET: ConnectWalletModal, + UNEXPECTED_ERROR: UnexpectedErrorModal, } as const export default function ModalRoot() { diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx index 313c41be9..45f32a250 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx @@ -4,17 +4,22 @@ import { useAppDispatch, useExecuteFunction, useFetchActivities, + useModal, useStakeFlowContext, } from "#/hooks" import { PROCESS_STATUSES } from "#/types" import { logPromiseFailure } from "#/utils" import { setStatus } from "#/store/action-flow" +import { UnexpectedErrorModalBase } from "#/components/UnexpectedErrorModal" import ServerErrorModal from "./ServerErrorModal" import RetryModal from "./RetryModal" import LoadingModal from "../../LoadingModal" -import UnexpectedErrorModal from "../../UnexpectedErrorModal" -export default function StakingErrorModal() { +export default function StakingErrorModal({ + closeModal, +}: { + closeModal: () => void +}) { const { stake } = useStakeFlowContext() const dispatch = useAppDispatch() const fetchActivities = useFetchActivities() @@ -54,5 +59,5 @@ export default function StakingErrorModal() { if (txHash) return - return + return } diff --git a/dapp/src/components/TransactionModal/ErrorModal.tsx b/dapp/src/components/TransactionModal/ErrorModal.tsx index 8c7f10a4a..9a775720a 100644 --- a/dapp/src/components/TransactionModal/ErrorModal.tsx +++ b/dapp/src/components/TransactionModal/ErrorModal.tsx @@ -1,9 +1,16 @@ import React from "react" import { ACTION_FLOW_TYPES, ActionFlowType } from "#/types" import StakingErrorModal from "./ActiveStakingStep/StakingErrorModal" -import UnexpectedErrorModal from "./UnexpectedErrorModal" +import { UnexpectedErrorModalBase } from "../UnexpectedErrorModal" -export default function ErrorModal({ type }: { type: ActionFlowType }) { - if (type === ACTION_FLOW_TYPES.STAKE) return - return +export default function ErrorModal({ + type, + closeModal, +}: { + type: ActionFlowType + closeModal: () => void +}) { + if (type === ACTION_FLOW_TYPES.STAKE) + return + return } diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx index b7eee010b..931f09d29 100644 --- a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx +++ b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx @@ -2,6 +2,7 @@ import { useActionFlowStatus, useActionFlowTokenAmount, useActionFlowType, + useModal, } from "#/hooks" import { PROCESS_STATUSES } from "#/types" import React from "react" @@ -20,6 +21,7 @@ export default function ModalContentWrapper({ const status = useActionFlowStatus() const type = useActionFlowType() const tokenAmount = useActionFlowTokenAmount() + const { closeModal } = useModal() if (!tokenAmount || status === PROCESS_STATUSES.REFINE_AMOUNT) return @@ -28,7 +30,8 @@ export default function ModalContentWrapper({ if (status === PROCESS_STATUSES.SUCCEEDED) return - if (status === PROCESS_STATUSES.FAILED) return + if (status === PROCESS_STATUSES.FAILED) + return if (status === PROCESS_STATUSES.PAUSED) return diff --git a/dapp/src/components/TransactionModal/UnexpectedErrorModal.tsx b/dapp/src/components/UnexpectedErrorModal.tsx similarity index 72% rename from dapp/src/components/TransactionModal/UnexpectedErrorModal.tsx rename to dapp/src/components/UnexpectedErrorModal.tsx index 362ad6a6a..da9090a68 100644 --- a/dapp/src/components/TransactionModal/UnexpectedErrorModal.tsx +++ b/dapp/src/components/UnexpectedErrorModal.tsx @@ -11,11 +11,13 @@ import { import { TextMd } from "#/components/shared/Typography" import { EXTERNAL_HREF } from "#/constants" import { IconBrandDiscordFilled } from "@tabler/icons-react" +import { BaseModalProps } from "#/types" +import withBaseModal from "./ModalRoot/withBaseModal" -export default function UnexpectedErrorModal() { +export function UnexpectedErrorModalBase({ withCloseButton }: BaseModalProps) { return ( <> - + {withCloseButton && } Unexpected error... @@ -38,3 +40,8 @@ export default function UnexpectedErrorModal() { ) } + +const UnexpectedErrorModal = withBaseModal(UnexpectedErrorModalBase, { + size: "lg", +}) +export default UnexpectedErrorModal diff --git a/dapp/src/types/modal.ts b/dapp/src/types/modal.ts index f561b0dd8..96986604d 100644 --- a/dapp/src/types/modal.ts +++ b/dapp/src/types/modal.ts @@ -2,6 +2,7 @@ export type ModalProps = Record export type BaseModalProps = { closeModal: () => void + withCloseButton?: boolean closeOnEsc?: boolean } @@ -11,6 +12,7 @@ export const MODAL_TYPES = { WELCOME: "WELCOME", MEZO_BEEHIVE: "MEZO_BEEHIVE", CONNECT_WALLET: "CONNECT_WALLET", + UNEXPECTED_ERROR: "UNEXPECTED_ERROR", } as const export type ModalType = (typeof MODAL_TYPES)[keyof typeof MODAL_TYPES] From ebf4c19232cfce0fcc530bcacf03685dd5048269 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 13:02:32 +0200 Subject: [PATCH 11/22] Show unexpected error modal when referral is invalid The application should be blocked when a user provides an intentionally incorrect referral. --- dapp/src/components/Header/ConnectWallet.tsx | 3 +- .../Header/Navigation/NavigationItem.tsx | 9 ++++- dapp/src/contexts/StakeFlowContext.tsx | 2 ++ dapp/src/hooks/useReferral.ts | 34 ++++++++++++------- dapp/src/utils/index.ts | 1 + dapp/src/utils/referralProgram.ts | 10 ++++++ 6 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 dapp/src/utils/referralProgram.ts diff --git a/dapp/src/components/Header/ConnectWallet.tsx b/dapp/src/components/Header/ConnectWallet.tsx index f7652c944..b3329af2d 100644 --- a/dapp/src/components/Header/ConnectWallet.tsx +++ b/dapp/src/components/Header/ConnectWallet.tsx @@ -45,7 +45,8 @@ export default function ConnectWallet() { pl={2} leftIcon={} onClick={handleConnectWallet} - {...(modalType === MODAL_TYPES.CONNECT_WALLET && { + {...((modalType === MODAL_TYPES.CONNECT_WALLET || + modalType === MODAL_TYPES.UNEXPECTED_ERROR) && { pointerEvents: "none", })} > diff --git a/dapp/src/components/Header/Navigation/NavigationItem.tsx b/dapp/src/components/Header/Navigation/NavigationItem.tsx index 9dd6806f0..f0f052363 100644 --- a/dapp/src/components/Header/Navigation/NavigationItem.tsx +++ b/dapp/src/components/Header/Navigation/NavigationItem.tsx @@ -9,6 +9,7 @@ import { motion } from "framer-motion" import { NavigationItemType } from "#/types/navigation" import { To, useSearchParams } from "react-router-dom" import { SEARCH_PARAMS_NAMES } from "#/router/path" +import { referralProgram } from "#/utils" import { NavLink } from "../../shared/NavLink" type NavigationItemProps = ListItemProps & NavigationItemType @@ -26,9 +27,15 @@ function NavigationItem(props: NavigationItemProps) { }), } + const isDisabled = !referralProgram.isValidReferral(Number(referralParam)) + return ( - + {({ isActive }) => ( <> {label} diff --git a/dapp/src/contexts/StakeFlowContext.tsx b/dapp/src/contexts/StakeFlowContext.tsx index a674d0526..f1dff62dd 100644 --- a/dapp/src/contexts/StakeFlowContext.tsx +++ b/dapp/src/contexts/StakeFlowContext.tsx @@ -30,6 +30,8 @@ export function StakeFlowProvider({ children }: { children: React.ReactNode }) { const initStake = useCallback(async () => { if (!acre) throw new Error("Acre SDK not defined") + if (!referral) throw new Error("Referral not defined") + await acreInitStake(referral, bitcoinRecoveryAddress) }, [acre, acreInitStake, bitcoinRecoveryAddress, referral]) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index a2a325082..8b04ce45a 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -1,12 +1,13 @@ import { env } from "#/constants" import { useCallback, useMemo } from "react" import { SEARCH_PARAMS_NAMES } from "#/router/path" +import { MODAL_TYPES } from "#/types" +import { referralProgram } from "#/utils" import useLocalStorage from "./useLocalStorage" - -const MAX_UINT16 = 65535 +import { useModal } from "./useModal" type UseReferralReturn = { - referral: number + referral?: number detectReferral: () => void resetReferral: () => void } @@ -16,22 +17,29 @@ export default function useReferral(): UseReferralReturn { "referral", env.REFERRAL, ) + const { openModal } = useModal() const detectReferral = useCallback(() => { const params = new URLSearchParams(window.location.search) const param = params.get(SEARCH_PARAMS_NAMES.referral) - const detectedReferral = param ? parseInt(param, 10) : null - - if ( - detectedReferral && - detectedReferral >= 0 && - detectedReferral <= MAX_UINT16 - ) { - setReferral(detectedReferral) - } else { + + if (param === null) { setReferral(env.REFERRAL) + return } - }, [setReferral]) + + const convertedReferral = Number(param) + + if (referralProgram.isValidReferral(convertedReferral)) { + setReferral(convertedReferral) + } else { + setReferral(undefined) + openModal(MODAL_TYPES.UNEXPECTED_ERROR, { + withCloseButton: false, + closeOnEsc: false, + }) + } + }, [openModal, setReferral]) const resetReferral = useCallback(() => { setReferral(env.REFERRAL) diff --git a/dapp/src/utils/index.ts b/dapp/src/utils/index.ts index d4b7fce95..afcb4a9ef 100644 --- a/dapp/src/utils/index.ts +++ b/dapp/src/utils/index.ts @@ -14,3 +14,4 @@ export * from "./type-check" export { default as eip1193 } from "./eip1193" export { default as orangeKit } from "./orangeKit" export { default as userAgent } from "./userAgent" +export { default as referralProgram } from "./referralProgram" diff --git a/dapp/src/utils/referralProgram.ts b/dapp/src/utils/referralProgram.ts new file mode 100644 index 000000000..5c044e433 --- /dev/null +++ b/dapp/src/utils/referralProgram.ts @@ -0,0 +1,10 @@ +const MAX_UINT16 = 65535 + +const isValidReferral = (value: number) => { + const isInteger = Number.isInteger(value) + return isInteger && value >= 0 && value <= MAX_UINT16 +} + +export default { + isValidReferral, +} From 57e1cb660ed326eb08f028582f96e90f19decc83 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 14:16:35 +0200 Subject: [PATCH 12/22] Navigate to subpage with the referral search param When referral is set it should be a constant parameter in the URL. Therefore, a special hook was created to set this parameter in the navigate function. --- .../TransactionModal/SuccessModal.tsx | 4 ++-- dapp/src/hooks/router/index.ts | 1 + dapp/src/hooks/router/useAppNavigate.ts | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 dapp/src/hooks/router/useAppNavigate.ts diff --git a/dapp/src/components/TransactionModal/SuccessModal.tsx b/dapp/src/components/TransactionModal/SuccessModal.tsx index cf104d597..0b22a52df 100644 --- a/dapp/src/components/TransactionModal/SuccessModal.tsx +++ b/dapp/src/components/TransactionModal/SuccessModal.tsx @@ -16,10 +16,10 @@ import { useModal, useAllActivitiesCount, useFetchActivities, + useAppNavigate, } from "#/hooks" import { CurrencyBalanceWithConversion } from "#/components/shared/CurrencyBalanceWithConversion" import { ACTION_FLOW_TYPES, ActionFlowType, MODAL_TYPES } from "#/types" -import { useNavigate } from "react-router-dom" import { routerPath } from "#/router/path" import { IconArrowUpRight } from "@tabler/icons-react" import { logPromiseFailure } from "#/utils" @@ -105,7 +105,7 @@ type SuccessModalProps = { export default function SuccessModal({ type }: SuccessModalProps) { const { closeModal, openModal } = useModal() const fetchActivities = useFetchActivities() - const navigate = useNavigate() + const navigate = useAppNavigate() const allActivitiesCount = useAllActivitiesCount() const { heading, footer, renderComponent } = CONTENT[type] diff --git a/dapp/src/hooks/router/index.ts b/dapp/src/hooks/router/index.ts index cb8162e91..5b97eb715 100644 --- a/dapp/src/hooks/router/index.ts +++ b/dapp/src/hooks/router/index.ts @@ -1 +1,2 @@ export * from "./useIsActiveRoute" +export { default as useAppNavigate } from "./useAppNavigate" diff --git a/dapp/src/hooks/router/useAppNavigate.ts b/dapp/src/hooks/router/useAppNavigate.ts new file mode 100644 index 000000000..ca262c2a9 --- /dev/null +++ b/dapp/src/hooks/router/useAppNavigate.ts @@ -0,0 +1,18 @@ +import { Pathname } from "#/router/path" +import { useCallback } from "react" +import { useNavigate, useSearchParams } from "react-router-dom" + +export default function useAppNavigate() { + const [searchParams] = useSearchParams() + const navigate = useNavigate() + + return useCallback( + (pathname: Pathname) => { + navigate({ + pathname, + search: searchParams.toString(), + }) + }, + [navigate, searchParams], + ) +} From fbdab500cbed8865a999118fe3db4e1ef34df602 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 14:22:59 +0200 Subject: [PATCH 13/22] Fix an eslint issue --- .../ActiveStakingStep/StakingErrorModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx index 45f32a250..e94cb4287 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakingErrorModal/index.tsx @@ -4,7 +4,6 @@ import { useAppDispatch, useExecuteFunction, useFetchActivities, - useModal, useStakeFlowContext, } from "#/hooks" import { PROCESS_STATUSES } from "#/types" From 6abe11a272a80c928cbd6ba7b852d326d092c052 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 14:47:19 +0200 Subject: [PATCH 14/22] Fix a failed build --- dapp/src/hooks/useReferral.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index 8b04ce45a..904ef5b87 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -33,7 +33,7 @@ export default function useReferral(): UseReferralReturn { if (referralProgram.isValidReferral(convertedReferral)) { setReferral(convertedReferral) } else { - setReferral(undefined) + setReferral(null) openModal(MODAL_TYPES.UNEXPECTED_ERROR, { withCloseButton: false, closeOnEsc: false, From 06f31e28ba1e296754d7fb79c39f6ca935c19bd2 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 14:50:01 +0200 Subject: [PATCH 15/22] Simplifying the use of searchParams --- dapp/src/components/Header/Navigation/NavigationItem.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dapp/src/components/Header/Navigation/NavigationItem.tsx b/dapp/src/components/Header/Navigation/NavigationItem.tsx index f0f052363..10c3d78fe 100644 --- a/dapp/src/components/Header/Navigation/NavigationItem.tsx +++ b/dapp/src/components/Header/Navigation/NavigationItem.tsx @@ -22,9 +22,7 @@ function NavigationItem(props: NavigationItemProps) { const referralParam = searchParams.get(SEARCH_PARAMS_NAMES.referral) const to: To = { pathname: href, - ...(referralParam && { - search: `?${SEARCH_PARAMS_NAMES.referral}=${referralParam}`, - }), + search: searchParams.toString(), } const isDisabled = !referralProgram.isValidReferral(Number(referralParam)) From 5a45b7fae84fd7f32e9063edeafbfaa83f89828a Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 14:52:33 +0200 Subject: [PATCH 16/22] Remove unnecessary parameter --- dapp/src/components/UnexpectedErrorModal.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dapp/src/components/UnexpectedErrorModal.tsx b/dapp/src/components/UnexpectedErrorModal.tsx index da9090a68..69890b84f 100644 --- a/dapp/src/components/UnexpectedErrorModal.tsx +++ b/dapp/src/components/UnexpectedErrorModal.tsx @@ -41,7 +41,5 @@ export function UnexpectedErrorModalBase({ withCloseButton }: BaseModalProps) { ) } -const UnexpectedErrorModal = withBaseModal(UnexpectedErrorModalBase, { - size: "lg", -}) +const UnexpectedErrorModal = withBaseModal(UnexpectedErrorModalBase) export default UnexpectedErrorModal From 565beb8c6536433cbb231695905676cd5dc27939 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 6 Aug 2024 14:59:09 +0200 Subject: [PATCH 17/22] Create a utils function for getting the referral form URL --- dapp/src/components/Header/Navigation/NavigationItem.tsx | 3 +-- dapp/src/hooks/useReferral.ts | 4 +--- dapp/src/utils/referralProgram.ts | 8 ++++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dapp/src/components/Header/Navigation/NavigationItem.tsx b/dapp/src/components/Header/Navigation/NavigationItem.tsx index 10c3d78fe..b1ead0a8b 100644 --- a/dapp/src/components/Header/Navigation/NavigationItem.tsx +++ b/dapp/src/components/Header/Navigation/NavigationItem.tsx @@ -8,7 +8,6 @@ import { import { motion } from "framer-motion" import { NavigationItemType } from "#/types/navigation" import { To, useSearchParams } from "react-router-dom" -import { SEARCH_PARAMS_NAMES } from "#/router/path" import { referralProgram } from "#/utils" import { NavLink } from "../../shared/NavLink" @@ -19,12 +18,12 @@ function NavigationItem(props: NavigationItemProps) { const styles = useMultiStyleConfig("Link", { variant: "navigation" }) const [searchParams] = useSearchParams() - const referralParam = searchParams.get(SEARCH_PARAMS_NAMES.referral) const to: To = { pathname: href, search: searchParams.toString(), } + const referralParam = referralProgram.getReferralFromURL() const isDisabled = !referralProgram.isValidReferral(Number(referralParam)) return ( diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index 904ef5b87..d0e45ed8d 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -1,6 +1,5 @@ import { env } from "#/constants" import { useCallback, useMemo } from "react" -import { SEARCH_PARAMS_NAMES } from "#/router/path" import { MODAL_TYPES } from "#/types" import { referralProgram } from "#/utils" import useLocalStorage from "./useLocalStorage" @@ -20,8 +19,7 @@ export default function useReferral(): UseReferralReturn { const { openModal } = useModal() const detectReferral = useCallback(() => { - const params = new URLSearchParams(window.location.search) - const param = params.get(SEARCH_PARAMS_NAMES.referral) + const param = referralProgram.getReferralFromURL() if (param === null) { setReferral(env.REFERRAL) diff --git a/dapp/src/utils/referralProgram.ts b/dapp/src/utils/referralProgram.ts index 5c044e433..b66eac663 100644 --- a/dapp/src/utils/referralProgram.ts +++ b/dapp/src/utils/referralProgram.ts @@ -1,3 +1,5 @@ +import { SEARCH_PARAMS_NAMES } from "#/router/path" + const MAX_UINT16 = 65535 const isValidReferral = (value: number) => { @@ -5,6 +7,12 @@ const isValidReferral = (value: number) => { return isInteger && value >= 0 && value <= MAX_UINT16 } +const getReferralFromURL = () => { + const params = new URLSearchParams(window.location.search) + return params.get(SEARCH_PARAMS_NAMES.referral) +} + export default { isValidReferral, + getReferralFromURL, } From da29068515b1d584af424dc82ad6a71158537e8e Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 7 Aug 2024 13:21:27 +0200 Subject: [PATCH 18/22] Fix an issue after merge --- dapp/src/components/TransactionModal/ModalContentWrapper.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx index dc94e8bc2..bf08288a4 100644 --- a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx +++ b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx @@ -2,7 +2,6 @@ import { useActionFlowStatus, useActionFlowTokenAmount, useActionFlowType, - useModal, } from "#/hooks" import { BaseModalProps, PROCESS_STATUSES } from "#/types" import React from "react" @@ -22,7 +21,6 @@ export default function ModalContentWrapper({ const status = useActionFlowStatus() const type = useActionFlowType() const tokenAmount = useActionFlowTokenAmount() - const { closeModal } = useModal() if (!tokenAmount || status === PROCESS_STATUSES.REFINE_AMOUNT) return From 6a6c0073df4387ecf52d8aca0f243c367c1e6d44 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 7 Aug 2024 13:24:02 +0200 Subject: [PATCH 19/22] Use `useAppNavigate` instead of `useNavigate` --- dapp/src/components/ModalRoot/withBaseModal.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dapp/src/components/ModalRoot/withBaseModal.tsx b/dapp/src/components/ModalRoot/withBaseModal.tsx index b16fd3848..8a75ebea3 100644 --- a/dapp/src/components/ModalRoot/withBaseModal.tsx +++ b/dapp/src/components/ModalRoot/withBaseModal.tsx @@ -1,8 +1,7 @@ import React, { ComponentType, useCallback } from "react" import { Modal, ModalContent, ModalOverlay, ModalProps } from "@chakra-ui/react" import { BaseModalProps } from "#/types" -import { useSidebar } from "#/hooks" -import { useNavigate } from "react-router-dom" +import { useAppNavigate, useSidebar } from "#/hooks" const MODAL_BASE_SIZE = "lg" @@ -14,7 +13,7 @@ function withBaseModal( const { closeModal, closeOnEsc, navigateToOnClose } = props const { isOpen: isSidebarOpen } = useSidebar() - const navigate = useNavigate() + const navigate = useAppNavigate() const handleCloseModal = useCallback(() => { closeModal() From 056cd500c513fd8f735af9a971f7a54f29765b5f Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 7 Aug 2024 13:31:34 +0200 Subject: [PATCH 20/22] Log an error to the console for incorrect referral --- dapp/src/hooks/useReferral.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index d0e45ed8d..6277a7c31 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -31,6 +31,7 @@ export default function useReferral(): UseReferralReturn { if (referralProgram.isValidReferral(convertedReferral)) { setReferral(convertedReferral) } else { + console.error("Incorrect referral") setReferral(null) openModal(MODAL_TYPES.UNEXPECTED_ERROR, { withCloseButton: false, From 13de34b2842e97a7c372c26d879f7128ac5ed914 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 7 Aug 2024 13:54:13 +0200 Subject: [PATCH 21/22] Fix the referral value check condition --- dapp/src/contexts/StakeFlowContext.tsx | 2 +- dapp/src/hooks/useReferral.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dapp/src/contexts/StakeFlowContext.tsx b/dapp/src/contexts/StakeFlowContext.tsx index f1dff62dd..f50c5b900 100644 --- a/dapp/src/contexts/StakeFlowContext.tsx +++ b/dapp/src/contexts/StakeFlowContext.tsx @@ -30,7 +30,7 @@ export function StakeFlowProvider({ children }: { children: React.ReactNode }) { const initStake = useCallback(async () => { if (!acre) throw new Error("Acre SDK not defined") - if (!referral) throw new Error("Referral not defined") + if (referral === null) throw new Error("Referral not defined") await acreInitStake(referral, bitcoinRecoveryAddress) }, [acre, acreInitStake, bitcoinRecoveryAddress, referral]) diff --git a/dapp/src/hooks/useReferral.ts b/dapp/src/hooks/useReferral.ts index 6277a7c31..26a3f53a7 100644 --- a/dapp/src/hooks/useReferral.ts +++ b/dapp/src/hooks/useReferral.ts @@ -6,7 +6,7 @@ import useLocalStorage from "./useLocalStorage" import { useModal } from "./useModal" type UseReferralReturn = { - referral?: number + referral: number | null detectReferral: () => void resetReferral: () => void } From e507fd934d99b3ff28ba9ccbd5cf100ce3a76ca6 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Thu, 8 Aug 2024 09:17:35 +0200 Subject: [PATCH 22/22] Make sure that the error modal will always be open We should not allow to open other modal windows when the global error modal is displayed. --- dapp/src/components/Header/ConnectWallet.tsx | 4 ++-- dapp/src/components/Header/Navigation/NavigationItem.tsx | 7 +++---- dapp/src/hooks/useModal.ts | 8 +++++++- dapp/src/store/modal/modalSelectors.ts | 5 ++++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/dapp/src/components/Header/ConnectWallet.tsx b/dapp/src/components/Header/ConnectWallet.tsx index b3329af2d..610bb7bc8 100644 --- a/dapp/src/components/Header/ConnectWallet.tsx +++ b/dapp/src/components/Header/ConnectWallet.tsx @@ -27,7 +27,7 @@ const getCustomDataByAccount = ( export default function ConnectWallet() { const { isConnected, address, balance, onDisconnect } = useWallet() - const { modalType, openModal } = useModal() + const { isOpenGlobalErrorModal, modalType, openModal } = useModal() const { hasCopied, onCopy } = useClipboard(address ?? "") const customDataBtcAccount = getCustomDataByAccount(address) @@ -46,7 +46,7 @@ export default function ConnectWallet() { leftIcon={} onClick={handleConnectWallet} {...((modalType === MODAL_TYPES.CONNECT_WALLET || - modalType === MODAL_TYPES.UNEXPECTED_ERROR) && { + isOpenGlobalErrorModal) && { pointerEvents: "none", })} > diff --git a/dapp/src/components/Header/Navigation/NavigationItem.tsx b/dapp/src/components/Header/Navigation/NavigationItem.tsx index b1ead0a8b..a010e401e 100644 --- a/dapp/src/components/Header/Navigation/NavigationItem.tsx +++ b/dapp/src/components/Header/Navigation/NavigationItem.tsx @@ -8,7 +8,7 @@ import { import { motion } from "framer-motion" import { NavigationItemType } from "#/types/navigation" import { To, useSearchParams } from "react-router-dom" -import { referralProgram } from "#/utils" +import { useModal } from "#/hooks" import { NavLink } from "../../shared/NavLink" type NavigationItemProps = ListItemProps & NavigationItemType @@ -17,15 +17,14 @@ function NavigationItem(props: NavigationItemProps) { const { label, href, ...restProps } = props const styles = useMultiStyleConfig("Link", { variant: "navigation" }) const [searchParams] = useSearchParams() + const { isOpenGlobalErrorModal } = useModal() + const isDisabled = isOpenGlobalErrorModal const to: To = { pathname: href, search: searchParams.toString(), } - const referralParam = referralProgram.getReferralFromURL() - const isDisabled = !referralProgram.isValidReferral(Number(referralParam)) - return ( { + // We should not allow to open other modal windows when the global error modal is displayed. + if (isOpenGlobalErrorModal) return + dispatch(openModal({ modalType: type, props })) }, - [dispatch], + [dispatch, isOpenGlobalErrorModal], ) const handleCloseModal = useCallback(() => dispatch(closeModal()), [dispatch]) @@ -26,6 +31,7 @@ export function useModal() { return { modalType, modalProps, + isOpenGlobalErrorModal, openModal: handleOpenModal, closeModal: handleCloseModal, } diff --git a/dapp/src/store/modal/modalSelectors.ts b/dapp/src/store/modal/modalSelectors.ts index 39f642e8e..d5271b952 100644 --- a/dapp/src/store/modal/modalSelectors.ts +++ b/dapp/src/store/modal/modalSelectors.ts @@ -1,4 +1,4 @@ -import { ModalProps, ModalType } from "#/types" +import { MODAL_TYPES, ModalProps, ModalType } from "#/types" import { RootState } from ".." export const selectModalType = (state: RootState): ModalType | null => @@ -6,3 +6,6 @@ export const selectModalType = (state: RootState): ModalType | null => export const selectModalProps = (state: RootState): ModalProps | undefined => state.modal.props + +export const selectIsOpenGlobalErrorModal = (state: RootState): boolean => + state.modal.modalType === MODAL_TYPES.UNEXPECTED_ERROR