From 9241e090840d409786eab18303fba047b7b3d97e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?= <33005392+nguyenhoaidanh@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:30:44 +0700 Subject: [PATCH 1/9] fix: issue limit order & add tracking (#2289) --- .../LimitOrder/ActionButtonLimitOrder.tsx | 11 ++-- .../swapv2/LimitOrder/EditOrderModal.tsx | 9 +-- .../swapv2/LimitOrder/LimitOrderForm.tsx | 55 +++++++++++-------- .../ListOrder/useRequestCancelOrder.tsx | 15 ++++- .../LimitOrder/Modals/CancelButtons.tsx | 38 +++++++++++-- .../LimitOrder/Modals/CancelOrderModal.tsx | 8 ++- src/components/swapv2/LimitOrder/helpers.ts | 3 +- src/components/swapv2/LimitOrder/type.ts | 7 ++- .../LimitOrder/useFetchActiveAllOrders.ts | 7 ++- src/hooks/useMixpanel.ts | 10 ++++ 10 files changed, 117 insertions(+), 46 deletions(-) diff --git a/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx b/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx index bdf2c18562..080cf51828 100644 --- a/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx +++ b/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx @@ -12,6 +12,7 @@ import { } from 'components/Button' import ProgressSteps from 'components/ProgressSteps' import { RowBetween } from 'components/Row' +import { EditOrderInfo } from 'components/swapv2/LimitOrder/type' import { useActiveWeb3React } from 'hooks' import { ApprovalState } from 'hooks/useApproveCallback' import { useWalletModalToggle } from 'state/application/hooks' @@ -34,7 +35,7 @@ export default function ActionButtonLimitOrder({ approvalSubmitted, showApproveFlow, showWarning, - isEdit, + editOrderInfo, }: { currencyIn: Currency | undefined currencyOut: Currency | undefined @@ -52,11 +53,13 @@ export default function ActionButtonLimitOrder({ approveCallback: () => Promise onWrapToken: () => Promise showPreview: () => void - isEdit: boolean + editOrderInfo?: EditOrderInfo }) { + const { isEdit, renderCancelButtons } = editOrderInfo || {} const disableBtnApproved = approval === ApprovalState.PENDING || - ((approval !== ApprovalState.NOT_APPROVED || approvalSubmitted || !!hasInputError) && enoughAllowance) + !!hasInputError || + ((approval !== ApprovalState.NOT_APPROVED || approvalSubmitted) && enoughAllowance) const disableBtnReview = checkingAllowance || @@ -115,7 +118,7 @@ export default function ActionButtonLimitOrder({ ) if (isEdit) { - return null + return checkingAllowance ? {contentButton} : renderCancelButtons?.() || null } if (showWarning && !disableBtnReview) return {contentButton} diff --git a/src/components/swapv2/LimitOrder/EditOrderModal.tsx b/src/components/swapv2/LimitOrder/EditOrderModal.tsx index 058d39bb91..95c5e2bb5e 100644 --- a/src/components/swapv2/LimitOrder/EditOrderModal.tsx +++ b/src/components/swapv2/LimitOrder/EditOrderModal.tsx @@ -88,7 +88,7 @@ export default function EditOrderModal({ ) const isSupportSoftCancelOrder = useIsSupportSoftCancelOrder() - const supportGasLessCancel = isSupportSoftCancelOrder(order) + const { orderSupportGasless: supportGasLessCancel, chainSupportGasless } = isSupportSoftCancelOrder(order) const [cancelType, setCancelType] = useState(CancelOrderType.GAS_LESS_CANCEL) useEffect(() => { setCancelType(supportGasLessCancel ? CancelOrderType.GAS_LESS_CANCEL : CancelOrderType.HARD_CANCEL) @@ -98,7 +98,6 @@ export default function EditOrderModal({ const estimateGas = useEstimateFee({ orders }) - const editOrderInfo: EditOrderInfo = { isEdit: true, gasFee: estimateGas, cancelType } const theme = useTheme() const isReviewOrder = step === Steps.REVIEW_ORDER const onBack = () => { @@ -139,8 +138,10 @@ export default function EditOrderModal({ onDismiss={onDismiss} onClickGaslessCancel={onClickGaslessCancel} onClickHardCancel={onClickHardCancel} + order={order} buttonInfo={{ - supportGasLessCancel, + orderSupportGasless: supportGasLessCancel, + chainSupportGasless, disabledGasLessCancel, disabledHardCancel, cancelGaslessText: Gasless Edit, @@ -153,6 +154,7 @@ export default function EditOrderModal({ ) } + const editOrderInfo: EditOrderInfo = { isEdit: true, gasFee: estimateGas, cancelType, renderCancelButtons } return ( @@ -193,7 +195,6 @@ export default function EditOrderModal({ defaultExpire={defaultExpire} /> )} - {renderCancelButtons()} ) diff --git a/src/components/swapv2/LimitOrder/LimitOrderForm.tsx b/src/components/swapv2/LimitOrder/LimitOrderForm.tsx index dcdacc5228..56f32b4968 100644 --- a/src/components/swapv2/LimitOrder/LimitOrderForm.tsx +++ b/src/components/swapv2/LimitOrder/LimitOrderForm.tsx @@ -583,7 +583,6 @@ const LimitOrderForm = forwardRef(function LimitOrd !checkingAllowance && !showWrap && !isNotFillAllInput && - !hasInputError && (approval === ApprovalState.NOT_APPROVED || approval === ApprovalState.PENDING || !enoughAllowance || @@ -601,6 +600,7 @@ const LimitOrderForm = forwardRef(function LimitOrd hasChangedOrderInfo() { return ( isEdit && + !hasInputError && (defaultInputAmount !== inputAmount || defaultRate?.rate !== rateInfo.rate || defaultExpire?.getTime() !== expiredAt) @@ -608,6 +608,29 @@ const LimitOrderForm = forwardRef(function LimitOrd }, })) + const renderActionBtn = () => ( + 0, + editOrderInfo, + }} + /> + ) const renderConfirmModal = (showConfirmContent = false) => ( (function LimitOrd /> ) - if (isEdit && flowState.showConfirm) return renderConfirmModal(true) + if (isEdit && flowState.showConfirm) + return ( + <> + {renderConfirmModal(true)} + {renderActionBtn()} + + ) return ( <> @@ -779,27 +808,7 @@ const LimitOrderForm = forwardRef(function LimitOrd ))} - 0, - isEdit, - }} - /> + {renderActionBtn()} {renderConfirmModal()} diff --git a/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx b/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx index a4cc61b863..adba2da856 100644 --- a/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx +++ b/src/components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder.tsx @@ -219,19 +219,29 @@ export const useProcessCancelOrder = ({ getOrders: (v: boolean) => LimitOrder[] isEdit?: boolean }) => { + const { chainId } = useActiveWeb3React() const [expiredTime, setExpiredTime] = useState(0) const [cancelStatus, setCancelStatus] = useState(CancelStatus.WAITING) const controller = useRef(new AbortController()) + const onResetState = useCallback(() => { + setExpiredTime(0) + setCancelStatus(CancelStatus.WAITING) + }, []) + useEffect(() => { if (!isOpen) { - setCancelStatus(CancelStatus.WAITING) + onResetState() } return () => { controller?.current?.abort() controller.current = new AbortController() } - }, [isOpen]) + }, [isOpen, onResetState]) + + useEffect(() => { + onResetState() + }, [chainId, onResetState]) const requestCancel = async (type: CancelOrderType) => { const signal = controller.current.signal @@ -246,6 +256,7 @@ export const useProcessCancelOrder = ({ else onDismiss() } catch (error) { if (signal.aborted) return + setExpiredTime(0) setCancelStatus(expiredTime ? CancelStatus.COUNTDOWN : CancelStatus.WAITING) } } diff --git a/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx b/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx index de1a78696e..76463ef465 100644 --- a/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx +++ b/src/components/swapv2/LimitOrder/Modals/CancelButtons.tsx @@ -11,7 +11,10 @@ import { GasStation } from 'components/Icons' import { MouseoverTooltip } from 'components/Tooltip' import { CancelStatus } from 'components/swapv2/LimitOrder/Modals/CancelOrderModal' import { DOCS_LINKS } from 'components/swapv2/LimitOrder/const' -import { CancelOrderType } from 'components/swapv2/LimitOrder/type' +import { getPayloadTracking } from 'components/swapv2/LimitOrder/helpers' +import { CancelOrderType, LimitOrder } from 'components/swapv2/LimitOrder/type' +import { useActiveWeb3React } from 'hooks' +import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' import { ExternalLink } from 'theme' import { formatDisplayNumber } from 'utils/numbers' @@ -73,7 +76,8 @@ const ButtonWrapper = styled.div` ` type ButtonInfo = { - supportGasLessCancel: boolean + orderSupportGasless: boolean + chainSupportGasless: boolean disabledGasLessCancel: boolean disabledHardCancel: boolean cancelGaslessText: ReactNode @@ -93,8 +97,10 @@ const CancelButtons = ({ confirmOnly = false, cancelType, setCancelType, + order, buttonInfo: { - supportGasLessCancel, + orderSupportGasless, + chainSupportGasless, disabledGasLessCancel = false, disabledHardCancel = false, cancelGaslessText, @@ -114,12 +120,26 @@ const CancelButtons = ({ cancelType: CancelOrderType setCancelType: (v: CancelOrderType) => void buttonInfo: ButtonInfo + order: LimitOrder | undefined }) => { const theme = useTheme() const isWaiting = cancelStatus === CancelStatus.WAITING const isCountDown = cancelStatus === CancelStatus.COUNTDOWN const isTimeout = cancelStatus === CancelStatus.TIMEOUT const isCancelDone = cancelStatus === CancelStatus.CANCEL_DONE + const { mixpanelHandler } = useMixpanel() + const { networkInfo } = useActiveWeb3React() + + const onSetType = (type: CancelOrderType) => { + setCancelType(type) + if (!order) return + mixpanelHandler( + isEdit ? MIXPANEL_TYPE.LO_CLICK_UPDATE_TYPE : MIXPANEL_TYPE.LO_CLICK_CANCEL_TYPE, + getPayloadTracking(order, networkInfo.name, { + [isEdit ? 'edit_type' : 'cancel_type']: type === CancelOrderType.GAS_LESS_CANCEL ? 'Gasless' : 'Hard', + }), + ) + } const gasAmountDisplay = estimateGas ? `~${formatDisplayNumber(estimateGas + '', { @@ -187,11 +207,17 @@ const CancelButtons = ({ buttonGasless={ setCancelType(CancelOrderType.GAS_LESS_CANCEL)} + onClick={() => onSetType(CancelOrderType.GAS_LESS_CANCEL)} $disabled={disabledGasLessCancel} disabled={disabledGasLessCancel} > @@ -204,7 +230,7 @@ const CancelButtons = ({ buttonHardEdit={ setCancelType(CancelOrderType.HARD_CANCEL)} + onClick={() => onSetType(CancelOrderType.HARD_CANCEL)} color={cancelType === CancelOrderType.HARD_CANCEL ? theme.primary : undefined} > diff --git a/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx b/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx index 9c0a583bb0..b15c955bbf 100644 --- a/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx +++ b/src/components/swapv2/LimitOrder/Modals/CancelOrderModal.tsx @@ -59,8 +59,10 @@ function CancelOrderModal({ const { orders = [], ordersSoftCancel = [], supportCancelGaslessAllOrders } = useAllActiveOrders(!isCancelAll) const isOrderSupportGaslessCancel = useIsSupportSoftCancelOrder() + const { orderSupportGasless, chainSupportGasless } = isOrderSupportGaslessCancel(order) + + const supportGasLessCancel = isCancelAll ? supportCancelGaslessAllOrders : orderSupportGasless - const supportGasLessCancel = isCancelAll ? supportCancelGaslessAllOrders : isOrderSupportGaslessCancel(order) const { onClickGaslessCancel, onClickHardCancel, expiredTime, cancelStatus, setCancelStatus } = useProcessCancelOrder( { isOpen, @@ -182,11 +184,13 @@ function CancelOrderModal({ flowState={flowState} /> { } } -export const getPayloadTracking = (order: LimitOrder, networkName: string) => { +export const getPayloadTracking = (order: LimitOrder, networkName: string, payload = {}) => { const { makerAssetSymbol, takerAssetSymbol, makingAmount, makerAssetDecimals, id } = order return { + ...payload, from_token: makerAssetSymbol, to_token: takerAssetSymbol, from_network: networkName, diff --git a/src/components/swapv2/LimitOrder/type.ts b/src/components/swapv2/LimitOrder/type.ts index f185d2307f..4a86bff0e2 100644 --- a/src/components/swapv2/LimitOrder/type.ts +++ b/src/components/swapv2/LimitOrder/type.ts @@ -66,7 +66,12 @@ export type CancelOrderFunction = (data: { isEdit?: boolean }) => Promise -export type EditOrderInfo = { cancelType?: CancelOrderType; gasFee?: string; isEdit?: boolean } +export type EditOrderInfo = { + cancelType?: CancelOrderType + gasFee?: string + isEdit?: boolean + renderCancelButtons: () => JSX.Element +} export type CancelOrderResponse = { orders: { operatorSignatureExpiredAt: number }[] diff --git a/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts b/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts index 87f869ea6e..1819a8fd6f 100644 --- a/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts +++ b/src/components/swapv2/LimitOrder/useFetchActiveAllOrders.ts @@ -9,9 +9,10 @@ export const useIsSupportSoftCancelOrder = () => { const { currentData: config } = useGetLOConfigQuery(chainId) return useCallback( (order: LimitOrder | undefined) => { - if (!order) return false const features = config?.features || {} - return !!features?.[order.contractAddress?.toLowerCase?.()]?.supportDoubleSignature + const orderSupportGasless = !!features?.[order?.contractAddress?.toLowerCase?.() ?? '']?.supportDoubleSignature + const chainSupportGasless = Object.values(features).some(e => e.supportDoubleSignature) + return { orderSupportGasless, chainSupportGasless } }, [config], ) @@ -27,7 +28,7 @@ export default function useAllActiveOrders(disabled = false) { const isSupportSoftCancel = useIsSupportSoftCancelOrder() return useMemo(() => { const orders = data?.orders || [] - const ordersSoftCancel = orders.filter(isSupportSoftCancel) + const ordersSoftCancel = orders.filter(e => isSupportSoftCancel(e).orderSupportGasless) return { orders, ordersSoftCancel, diff --git a/src/hooks/useMixpanel.ts b/src/hooks/useMixpanel.ts index cda967d80e..16b69267be 100644 --- a/src/hooks/useMixpanel.ts +++ b/src/hooks/useMixpanel.ts @@ -201,6 +201,8 @@ export enum MIXPANEL_TYPE { LO_CLICK_EDIT_ORDER, LO_DISPLAY_SETTING_CLICK, LO_CLICK_SUBSCRIBE_BTN, + LO_CLICK_CANCEL_TYPE, + LO_CLICK_UPDATE_TYPE, // Wallet UI WUI_WALLET_CLICK, @@ -1115,6 +1117,14 @@ export default function useMixpanel(currencies?: { [field in Field]?: Currency } mixpanel.track('Limit Order - User click to Subscribe button') break } + case MIXPANEL_TYPE.LO_CLICK_CANCEL_TYPE: { + mixpanel.track('Limit Order - Cancel Order Double Signature Click', payload) + break + } + case MIXPANEL_TYPE.LO_CLICK_UPDATE_TYPE: { + mixpanel.track('Limit Order - Update Order Double Signature Click', payload) + break + } case MIXPANEL_TYPE.WUI_WALLET_CLICK: { mixpanel.track('Wallet UI - Wallet Click') From 1e9f29183732be5cca8bf2f8b267c634f8808e42 Mon Sep 17 00:00:00 2001 From: Nguyen Van Viet Date: Thu, 12 Oct 2023 17:13:17 +0700 Subject: [PATCH 2/9] chore: update text for price deviate KEP-1693 (#2293) --- src/pages/AddLiquidityV2/index.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pages/AddLiquidityV2/index.tsx b/src/pages/AddLiquidityV2/index.tsx index 892e27f014..645db2817e 100644 --- a/src/pages/AddLiquidityV2/index.tsx +++ b/src/pages/AddLiquidityV2/index.tsx @@ -704,6 +704,14 @@ export default function AddLiquidity() { const upToMedium = useMedia(`(max-width: ${MEDIA_WIDTHS.upToMedium}px)`) const upToXXSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToXXSmall}px)`) + const priceDiff = + baseCurrency && quoteCurrency && tokenA && tokenB && price + ? Math.abs( + Number((isSorted ? price : price?.invert())?.toSignificant(18)) / + (usdPrices[tokenA.wrapped.address] / usdPrices[tokenB.wrapped.address]) - + 1, + ) + : 0 const isPriceDeviated = baseCurrency && quoteCurrency && @@ -715,6 +723,7 @@ export default function AddLiquidity() { (usdPrices[tokenA.wrapped.address] / usdPrices[tokenB.wrapped.address]) - 1, ) >= 0.02 + const isFullRange = activeRange === RANGE.FULL_RANGE const isValid = !errorMessage && !invalidRange const isWarningButton = isPriceDeviated || isFullRange || outOfRange @@ -857,7 +866,10 @@ export default function AddLiquidity() { {formatDisplayNumber(usdPrices[tokenA.wrapped.address] / usdPrices[tokenB.wrapped.address], { significantDigits: 4, })}{' '} - {quoteCurrency.symbol}). You might have high impermanent loss after you add liquidity to this pool + {quoteCurrency.symbol}) by {(priceDiff * 100).toFixed(2)}%. Please consider the{' '} + + impermanent loss + )} From 21508d9fb81d154436125c5f0223b45fe053530e Mon Sep 17 00:00:00 2001 From: nhd98z <34548156+nhd98z@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:13:47 +0700 Subject: [PATCH 3/9] Fix option title (#2292) --- src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx b/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx index 805fd21f83..624ed3c565 100644 --- a/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx +++ b/src/pages/KyberDAO/Vote/ProposalItem/Participants.tsx @@ -166,7 +166,9 @@ export default function Participants({ proposalId }: { proposalId?: number }) { } }) }, [proposalInfo]) - const options = proposalInfo?.options + const options = proposalInfo?.options.map( + (option, index) => (proposalId ? HARDCODED_OPTION_TITLE[proposalId] : {})?.[index] || option, + ) // flag to reduce fontsize when contents are too long const isLongText = useMemo(() => { From 4cb133c0031ba15b7abc2c7a47387ae0a14de019 Mon Sep 17 00:00:00 2001 From: ltthienn <132639843+ltthienn@users.noreply.github.com> Date: Fri, 13 Oct 2023 13:54:41 +0700 Subject: [PATCH 4/9] E2E | Limit order - Able to select token (#2294) QA-57 | Add limit order tests --- cypress/e2e/pages/limit-order.po.cy.ts | 24 ++++++ cypress/e2e/pages/swap-page.po.cy.ts | 20 +++-- cypress/e2e/selectors/selectors.cy.ts | 6 ++ cypress/e2e/specs/limit-order.e2e.cy.ts | 86 +++++++++++++++++++ cypress/e2e/specs/swap-page.e2e.cy.ts | 4 +- cypress/support/selectTokenCommands.ts | 12 +-- .../swapv2/LimitOrder/LimitOrderForm.tsx | 2 + src/pages/SwapV3/Tabs/LimitTab.tsx | 2 +- 8 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 cypress/e2e/pages/limit-order.po.cy.ts create mode 100644 cypress/e2e/specs/limit-order.e2e.cy.ts diff --git a/cypress/e2e/pages/limit-order.po.cy.ts b/cypress/e2e/pages/limit-order.po.cy.ts new file mode 100644 index 0000000000..6b75549119 --- /dev/null +++ b/cypress/e2e/pages/limit-order.po.cy.ts @@ -0,0 +1,24 @@ +import { LimitOrderLocators } from "../selectors/selectors.cy" +import { TokenCatalog } from "./swap-page.po.cy" +export interface myCallbackType { + (myArgument: T): void +} +export const LimitOder = { + + selectTokenSell(): TokenCatalog { + cy.selectToken(LimitOrderLocators.dropdownTokenSell) + return new TokenCatalog() + }, + selectTokenBuy(): TokenCatalog { + cy.selectToken(LimitOrderLocators.dropdownTokenBuy) + return new TokenCatalog() + }, + + getCurrentTokenSell(text: myCallbackType) { + cy.getContent(LimitOrderLocators.dropdownTokenSell, text) + }, + + getCurrentTokenBuy(text: myCallbackType) { + cy.getContent(LimitOrderLocators.dropdownTokenBuy, text) + }, +} diff --git a/cypress/e2e/pages/swap-page.po.cy.ts b/cypress/e2e/pages/swap-page.po.cy.ts index 31dc3d5fe6..a45936195a 100644 --- a/cypress/e2e/pages/swap-page.po.cy.ts +++ b/cypress/e2e/pages/swap-page.po.cy.ts @@ -1,4 +1,4 @@ -import { HeaderLocators, NetworkLocators, SwapPageLocators, TokenCatalogLocators, WalletLocators } from "../selectors/selectors.cy" +import { HeaderLocators, LimitOrderLocators, NetworkLocators, SwapPageLocators, TokenCatalogLocators, WalletLocators } from "../selectors/selectors.cy" export interface myCallbackType { (myArgument: T): void @@ -12,11 +12,11 @@ export const SwapPage = { }, selectTokenIn(): TokenCatalog { - cy.selectTokenIn() + cy.selectToken(SwapPageLocators.dropdownTokenIn) return new TokenCatalog() }, selectTokenOut(): TokenCatalog { - cy.selectTokenOut() + cy.selectToken(SwapPageLocators.dropdownTokenOut) return new TokenCatalog() }, @@ -37,6 +37,10 @@ export const SwapPage = { cy.get(WalletLocators.statusConnected, { timeout: 10000 }).should('be.visible') }, + goToLimitOrder() { + cy.get(LimitOrderLocators.btnLimit).click() + }, + goToFarmPage() { cy.get(HeaderLocators.dropdownEarn).click({ force: true }) cy.get(HeaderLocators.lblFarms).click({ force: true }) @@ -78,10 +82,12 @@ export class TokenCatalog { cy.selectTokenBySymbol(tokenSymbol) } - addFavoriteToken(tokenSymbol: string) { - this.searchToken(tokenSymbol) - cy.wait(2000) - cy.addFavoriteToken() + addFavoriteToken(tokenSymbol: Array) { + tokenSymbol.forEach(element => { + this.searchToken(element) + cy.wait(2000) + cy.addFavoriteToken() + }); } removeFavoriteToken(tokenSymbol: string) { diff --git a/cypress/e2e/selectors/selectors.cy.ts b/cypress/e2e/selectors/selectors.cy.ts index f3a85e1eb3..4451a4168f 100644 --- a/cypress/e2e/selectors/selectors.cy.ts +++ b/cypress/e2e/selectors/selectors.cy.ts @@ -21,6 +21,12 @@ export const SwapPageLocators = { btnSkipTutorial: '[data-testid=button-skip-tutorial]', } +export const LimitOrderLocators = { + dropdownTokenSell: '[data-testid=limit-order-input-tokena] [data-testid=token-symbol-container]', + dropdownTokenBuy: '[data-testid=limit-order-input-tokenb] [data-testid=token-symbol-container]', + btnLimit: '[data-testid=limit-button]' +} + export const WalletLocators = { btnConnectWallet: '[data-testid=button-connect-wallet]', btnMetaMask: '[data-testid=connect-METAMASK]', diff --git a/cypress/e2e/specs/limit-order.e2e.cy.ts b/cypress/e2e/specs/limit-order.e2e.cy.ts new file mode 100644 index 0000000000..d77682cd85 --- /dev/null +++ b/cypress/e2e/specs/limit-order.e2e.cy.ts @@ -0,0 +1,86 @@ +import { LimitOder } from "../pages/limit-order.po.cy" +import { SwapPage, TokenCatalog } from "../pages/swap-page.po.cy" +import { DEFAULT_URL, NETWORK, NORESULTS_TEXT, NOTOKENS_TEXT, TAG, TOKEN_SYMBOLS, UNWHITELIST_SYMBOL_TOKENS, UNWHITELIST_TOKENS } from "../selectors/constants.cy" + +const unWhitelistTokens = UNWHITELIST_TOKENS[NETWORK] +const tokenSymbols = TOKEN_SYMBOLS[NETWORK] + +const arrAddress = [unWhitelistTokens[0].address, unWhitelistTokens[1].address, unWhitelistTokens[2].address] +const arrSymbol = [unWhitelistTokens[0].symbol, unWhitelistTokens[1].symbol, unWhitelistTokens[2].symbol] + + +const tokenCatalog = new TokenCatalog(); + + +describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { + beforeEach(() => { + SwapPage.open(DEFAULT_URL) + SwapPage.goToLimitOrder() + }) + + describe('Add/remove/select token with favorite tokens list', () => { + it('Should be added, selected and removed favorite token sell', () => { + LimitOder.selectTokenSell().addFavoriteToken([tokenSymbols[0], tokenSymbols[1]]) + tokenCatalog.getFavoriteTokens((list) => { + expect(list).to.include.members([tokenSymbols[1]]) + }) + + tokenCatalog.selectFavoriteToken(tokenSymbols[1]) + LimitOder.getCurrentTokenSell((text) => { + expect(text).to.equal(tokenSymbols[1]) + }) + LimitOder.selectTokenSell() + tokenCatalog.removeFavoriteToken(tokenSymbols[0]) + tokenCatalog.getFavoriteTokens((list) => { + expect(list).not.to.include.members([tokenSymbols[0]]) + }) + }) + + it('Should be added, selected and removed favorite token buy', () => { + LimitOder.selectTokenBuy().addFavoriteToken([tokenSymbols[0], tokenSymbols[1]]) + tokenCatalog.getFavoriteTokens((list) => { + expect(list).to.include.members([tokenSymbols[1]]) + }) + + tokenCatalog.selectFavoriteToken(tokenSymbols[1]) + LimitOder.getCurrentTokenBuy((text) => { + expect(text).to.equal(tokenSymbols[1]) + }) + LimitOder.selectTokenBuy() + tokenCatalog.removeFavoriteToken(tokenSymbols[0]) + tokenCatalog.getFavoriteTokens((list) => { + expect(list).not.to.include.members([tokenSymbols[0]]) + }) + }) + }) + + describe('Select token by symbol', () => { + it('Should be selected token sell by symbol successfully', () => { + LimitOder.selectTokenSell().selectTokenBySymbol(tokenSymbols[0]) + LimitOder.getCurrentTokenSell((text) => { + expect(text).to.equal(tokenSymbols[0]) + }) + }) + + it('Should be selected token buy by symbol successfully', () => { + LimitOder.selectTokenBuy().selectTokenBySymbol(tokenSymbols[1]) + LimitOder.getCurrentTokenBuy((text) => { + expect(text).to.equal(tokenSymbols[1]) + }) + }) + + it('Should be unselected tokenIn not exist in whitelist', () => { + LimitOder.selectTokenSell().searchToken(UNWHITELIST_SYMBOL_TOKENS[0]) + tokenCatalog.getNoResultsFound((text) => { + expect(text).to.equal(NORESULTS_TEXT) + }) + }) + + it('Should be unselected tokenOut not exist in whitelist', () => { + LimitOder.selectTokenBuy().searchToken(UNWHITELIST_SYMBOL_TOKENS[0]) + tokenCatalog.getNoResultsFound((text) => { + expect(text).to.equal(NORESULTS_TEXT) + }) + }) + }) +}) \ No newline at end of file diff --git a/cypress/e2e/specs/swap-page.e2e.cy.ts b/cypress/e2e/specs/swap-page.e2e.cy.ts index 4c52d4e28b..a35d65057c 100644 --- a/cypress/e2e/specs/swap-page.e2e.cy.ts +++ b/cypress/e2e/specs/swap-page.e2e.cy.ts @@ -47,7 +47,7 @@ describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { }) it('Should be added tokenIn to favorite tokens list', () => { - SwapPage.selectTokenIn().addFavoriteToken(tokenSymbols[0]) + SwapPage.selectTokenIn().addFavoriteToken([tokenSymbols[0]]) tokenCatalog.getFavoriteTokens((list) => { expect(list).to.include.members([tokenSymbols[0]]) }) @@ -63,7 +63,7 @@ describe(`Token Catalog on ${NETWORK}`, { tags: TAG.regression }, () => { }) it('Should be added tokenOut to favorite tokens list', () => { - SwapPage.selectTokenOut().addFavoriteToken(tokenSymbols[0]) + SwapPage.selectTokenOut().addFavoriteToken([tokenSymbols[0]]) tokenCatalog.getFavoriteTokens((list) => { expect(list).to.include.members([tokenSymbols[0]]) }) diff --git a/cypress/support/selectTokenCommands.ts b/cypress/support/selectTokenCommands.ts index 098d334d3e..80d8eafaa3 100644 --- a/cypress/support/selectTokenCommands.ts +++ b/cypress/support/selectTokenCommands.ts @@ -10,8 +10,7 @@ declare global { interface Chainable { closeTutorialPopup(): Chainable searchToken(value: string): Chainable - selectTokenIn(): Chainable - selectTokenOut(): Chainable + selectToken(selector: string): Chainable selectTokenBySymbol(value: string): Chainable removeFavoriteToken(value: string): Chainable addFavoriteToken(): Chainable @@ -30,15 +29,12 @@ Cypress.Commands.add('closeTutorialPopup', () => { cy.get(SwapPageLocators.btnSkipTutorial, { timeout: 30000 }).should('be.visible').click() }) -Cypress.Commands.add('selectTokenIn', () => { - cy.get(SwapPageLocators.dropdownTokenIn, { timeout: 30000 }).should('be.visible').click() -}) - -Cypress.Commands.add('selectTokenOut', () => { - cy.get(SwapPageLocators.dropdownTokenOut, { timeout: 30000 }).should('be.visible').click() +Cypress.Commands.add('selectToken', (selector: string) => { + cy.get(selector, { timeout: 30000 }).should('be.visible').click() }) Cypress.Commands.add('searchToken', (value) => { + cy.get(TokenCatalogLocators.txtToken).clear() cy.get(TokenCatalogLocators.txtToken).should('be.visible').type(value) }) diff --git a/src/components/swapv2/LimitOrder/LimitOrderForm.tsx b/src/components/swapv2/LimitOrder/LimitOrderForm.tsx index 56f32b4968..3cae177bfb 100644 --- a/src/components/swapv2/LimitOrder/LimitOrderForm.tsx +++ b/src/components/swapv2/LimitOrder/LimitOrderForm.tsx @@ -676,6 +676,7 @@ const LimitOrderForm = forwardRef(function LimitOrd currency={currencyIn} showCommonBases id="create-limit-order-input-tokena" + dataTestId="limit-order-input-tokena" maxCurrencySymbolLength={6} filterWrap onClickSelect={trackingTouchSelectToken} @@ -703,6 +704,7 @@ const LimitOrderForm = forwardRef(function LimitOrd estimatedUsd={estimateUSD.output} onFocus={trackingTouchInput} id="create-limit-order-input-tokenb" + dataTestId="limit-order-input-tokenb" onCurrencySelect={handleOutputSelect} positionMax="top" showCommonBases diff --git a/src/pages/SwapV3/Tabs/LimitTab.tsx b/src/pages/SwapV3/Tabs/LimitTab.tsx index f2b1c1b2b5..c46bd4b04c 100644 --- a/src/pages/SwapV3/Tabs/LimitTab.tsx +++ b/src/pages/SwapV3/Tabs/LimitTab.tsx @@ -45,7 +45,7 @@ export default function LimitTab({ onClick }: Props) { } return ( - + Limit{' '} {numberOfInsufficientFundOrders ? ( From 0e82e562b67dac65677313f47d4f44a9583d3b24 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Fri, 13 Oct 2023 14:32:53 +0700 Subject: [PATCH 5/9] feat: update connect wallets (#2249) --- src/components/Header/web3/SelectWallet.tsx | 2 +- .../Header/web3/WalletModal/index.tsx | 46 ++++++------------- src/components/Menu/FaucetModal.tsx | 2 +- .../SwapForm/SwapActionButton/index.tsx | 2 +- src/components/YieldPools/ListItem.tsx | 2 +- .../LimitOrder/ActionButtonLimitOrder.tsx | 2 +- src/pages/AddLiquidity/TokenPair.tsx | 2 +- src/pages/AddLiquidity/ZapIn.tsx | 2 +- src/pages/AddLiquidityV2/index.tsx | 2 +- src/pages/Bridge/SwapForm.tsx | 2 +- src/pages/BuyCrypto/index.tsx | 2 +- src/pages/Campaign/CampaignContent.tsx | 2 +- src/pages/CreatePool/index.tsx | 2 +- src/pages/CrossChain/SwapForm/index.tsx | 2 +- src/pages/IncreaseLiquidity/index.tsx | 2 +- .../KyberDAO/KNCUtility/GasRefundBox.tsx | 2 +- src/pages/KyberDAO/StakeKNC/MigrateModal.tsx | 2 +- .../KyberDAO/StakeKNC/StakeKNCComponent.tsx | 4 +- .../KyberDAO/Vote/ProposalItem/index.tsx | 2 +- src/pages/KyberDAO/Vote/index.tsx | 2 +- src/pages/MyEarnings/Placeholder.tsx | 2 +- src/pages/RemoveLiquidity/TokenPair.tsx | 2 +- src/pages/RemoveLiquidity/ZapOut.tsx | 2 +- src/pages/SwapV2/index.tsx | 2 +- .../components/RequireConnectWallet.tsx | 2 +- src/state/burn/proamm/hooks.tsx | 2 +- src/state/mint/proamm/hooks.tsx | 4 +- src/state/user/hooks.tsx | 5 +- 28 files changed, 45 insertions(+), 62 deletions(-) diff --git a/src/components/Header/web3/SelectWallet.tsx b/src/components/Header/web3/SelectWallet.tsx index a29a92947a..094b1e455d 100644 --- a/src/components/Header/web3/SelectWallet.tsx +++ b/src/components/Header/web3/SelectWallet.tsx @@ -198,7 +198,7 @@ function Web3StatusInner() { id={TutorialIds.BUTTON_CONNECT_WALLET} data-testid="button-connect-wallet" > - Connect Wallet + Connect ) } diff --git a/src/components/Header/web3/WalletModal/index.tsx b/src/components/Header/web3/WalletModal/index.tsx index 6e23129f31..0986a50436 100644 --- a/src/components/Header/web3/WalletModal/index.tsx +++ b/src/components/Header/web3/WalletModal/index.tsx @@ -32,7 +32,6 @@ import { useWalletModalToggle, } from 'state/application/hooks' import { useIsConnectingWallet } from 'state/authen/hooks' -import { useIsAcceptedTerm } from 'state/user/hooks' import { ExternalLink } from 'theme' import { isEVMWallet, isOverriddenWallet, isSolanaWallet } from 'utils' @@ -62,7 +61,7 @@ const ContentWrapper = styled.div` ` const TermAndCondition = styled.div` - padding: 8px; + padding: 8px 16px; font-size: 12px; font-weight: 500; line-height: 16px; @@ -158,8 +157,6 @@ export default function WalletModal() { const previousAccount = usePrevious(account) - const [isAcceptedTerm, setIsAcceptedTerm] = useIsAcceptedTerm() - const location = useLocation() const { mixpanelHandler } = useMixpanel() @@ -316,36 +313,19 @@ export default function WalletModal() { {(walletView === WALLET_VIEWS.ACCOUNT || walletView === WALLET_VIEWS.CHANGE_WALLET) && ( - { - if (!isAcceptedTerm) { - mixpanelHandler(MIXPANEL_TYPE.WALLET_CONNECT_ACCEPT_TERM_CLICK) - } - setIsAcceptedTerm(!isAcceptedTerm) - }} - > - { - // - }} - type="checkbox" - checked={isAcceptedTerm} - data-testid="accept-term" - style={{ marginRight: '12px', height: '14px', width: '14px', minWidth: '14px', cursor: 'pointer' }} - /> + - Accept {' '} - e.stopPropagation()}> - KyberSwap‘s Terms of Use - {' '} - and{' '} - e.stopPropagation()}> - Privacy Policy - - {'. '} - - Last updated: {dayjs(TERM_FILES_PATH.VERSION).format('DD MMM YYYY')} - + + By connecting a wallet, you accept{' '} + e.stopPropagation()}> + KyberSwap‘s Terms of Use + {' '} + and consent to its{' '} + e.stopPropagation()}> + Privacy Policy + + . Last updated: {dayjs(TERM_FILES_PATH.VERSION).format('DD MMM YYYY')} + )} diff --git a/src/components/Menu/FaucetModal.tsx b/src/components/Menu/FaucetModal.tsx index 3a74446dfa..6e60406dd8 100644 --- a/src/components/Menu/FaucetModal.tsx +++ b/src/components/Menu/FaucetModal.tsx @@ -177,7 +177,7 @@ function FaucetModal() { }} style={{ borderRadius: '24px', height: '44px' }} > - Connect Wallet + Connect )} diff --git a/src/components/SwapForm/SwapActionButton/index.tsx b/src/components/SwapForm/SwapActionButton/index.tsx index bd418a4a99..a6557f8303 100644 --- a/src/components/SwapForm/SwapActionButton/index.tsx +++ b/src/components/SwapForm/SwapActionButton/index.tsx @@ -194,7 +194,7 @@ const SwapActionButton: React.FC = ({ if (!account) { return ( - Connect Wallet + Connect ) } diff --git a/src/components/YieldPools/ListItem.tsx b/src/components/YieldPools/ListItem.tsx index c242210afb..8bffef527c 100644 --- a/src/components/YieldPools/ListItem.tsx +++ b/src/components/YieldPools/ListItem.tsx @@ -708,7 +708,7 @@ const ListItem = ({ farm }: ListItemProps) => { {!account ? ( - Connect Wallet + Connect ) : ( approvalState === ApprovalState.UNKNOWN && diff --git a/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx b/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx index 080cf51828..a888ba69dc 100644 --- a/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx +++ b/src/components/swapv2/LimitOrder/ActionButtonLimitOrder.tsx @@ -75,7 +75,7 @@ export default function ActionButtonLimitOrder({ if (!account) return ( - Connect Wallet + Connect ) diff --git a/src/pages/AddLiquidity/TokenPair.tsx b/src/pages/AddLiquidity/TokenPair.tsx index 6098b2241d..17d934722c 100644 --- a/src/pages/AddLiquidity/TokenPair.tsx +++ b/src/pages/AddLiquidity/TokenPair.tsx @@ -682,7 +682,7 @@ const TokenPair = ({ {!account ? ( - Connect Wallet + Connect ) : ( diff --git a/src/pages/AddLiquidity/ZapIn.tsx b/src/pages/AddLiquidity/ZapIn.tsx index 5d61485169..25db3f3be8 100644 --- a/src/pages/AddLiquidity/ZapIn.tsx +++ b/src/pages/AddLiquidity/ZapIn.tsx @@ -727,7 +727,7 @@ const ZapIn = ({ {!account ? ( - Connect Wallet + Connect ) : ( diff --git a/src/pages/AddLiquidityV2/index.tsx b/src/pages/AddLiquidityV2/index.tsx index 645db2817e..a1fa8e9fa8 100644 --- a/src/pages/AddLiquidityV2/index.tsx +++ b/src/pages/AddLiquidityV2/index.tsx @@ -731,7 +731,7 @@ export default function AddLiquidity() { const Buttons = () => !account ? ( - Connect Wallet + Connect ) : ( - Connect Wallet + Connect ) : ( showApproveFlow && ( diff --git a/src/pages/BuyCrypto/index.tsx b/src/pages/BuyCrypto/index.tsx index a7d9680b72..e0400f8428 100644 --- a/src/pages/BuyCrypto/index.tsx +++ b/src/pages/BuyCrypto/index.tsx @@ -473,7 +473,7 @@ function BuyCrypto() { {!account ? ( - Connect your wallet + Connect ) : (
diff --git a/src/pages/Campaign/CampaignContent.tsx b/src/pages/Campaign/CampaignContent.tsx index 75d9c85f69..27a96703e6 100644 --- a/src/pages/Campaign/CampaignContent.tsx +++ b/src/pages/Campaign/CampaignContent.tsx @@ -571,7 +571,7 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp style={{ gridColumn: '1 / -1', padding: '8px', margin: '0', borderRadius: '18px' }} onClick={toggleWalletModal} > - Connect Wallet + Connect ) ) : ( diff --git a/src/pages/CreatePool/index.tsx b/src/pages/CreatePool/index.tsx index 7a6dd7ba4b..98ded952d9 100644 --- a/src/pages/CreatePool/index.tsx +++ b/src/pages/CreatePool/index.tsx @@ -744,7 +744,7 @@ export default function CreatePool() { {!account ? ( - Connect Wallet + Connect ) : ( diff --git a/src/pages/CrossChain/SwapForm/index.tsx b/src/pages/CrossChain/SwapForm/index.tsx index 89c84d1348..fb6e2165e4 100644 --- a/src/pages/CrossChain/SwapForm/index.tsx +++ b/src/pages/CrossChain/SwapForm/index.tsx @@ -386,7 +386,7 @@ export default function SwapForm() { /> ) : ( - Connect Wallet + Connect )} diff --git a/src/pages/IncreaseLiquidity/index.tsx b/src/pages/IncreaseLiquidity/index.tsx index 6d53224488..b54ee8d6e2 100644 --- a/src/pages/IncreaseLiquidity/index.tsx +++ b/src/pages/IncreaseLiquidity/index.tsx @@ -352,7 +352,7 @@ export default function IncreaseLiquidity() { ) : !account ? ( - Connect Wallet + Connect ) : ( <> diff --git a/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx b/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx index 13795b8949..3028053716 100644 --- a/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx +++ b/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx @@ -199,7 +199,7 @@ export default function GasRefundBox() { ) ) : ( - Connect Wallet + Connect ) ) : selectedTab === KNCUtilityTabs.Pending && nextCycleStartTime ? ( diff --git a/src/pages/KyberDAO/StakeKNC/MigrateModal.tsx b/src/pages/KyberDAO/StakeKNC/MigrateModal.tsx index 5b70004289..5e4881eb66 100644 --- a/src/pages/KyberDAO/StakeKNC/MigrateModal.tsx +++ b/src/pages/KyberDAO/StakeKNC/MigrateModal.tsx @@ -166,7 +166,7 @@ export default function MigrateModal({ ) : ( - Connect Wallet + Connect )} diff --git a/src/pages/KyberDAO/StakeKNC/StakeKNCComponent.tsx b/src/pages/KyberDAO/StakeKNC/StakeKNCComponent.tsx index 508b059a16..59350bfcef 100644 --- a/src/pages/KyberDAO/StakeKNC/StakeKNCComponent.tsx +++ b/src/pages/KyberDAO/StakeKNC/StakeKNCComponent.tsx @@ -529,7 +529,7 @@ export default function StakeKNCComponent() { style={{ marginRight: '5px' }} placement="top" /> - Connect Wallet + Connect )} @@ -603,7 +603,7 @@ export default function StakeKNCComponent() { style={{ marginRight: '5px' }} placement="top" /> - Connect Wallet + Connect )} diff --git a/src/pages/KyberDAO/Vote/ProposalItem/index.tsx b/src/pages/KyberDAO/Vote/ProposalItem/index.tsx index 6396527f5e..52066e78d7 100644 --- a/src/pages/KyberDAO/Vote/ProposalItem/index.tsx +++ b/src/pages/KyberDAO/Vote/ProposalItem/index.tsx @@ -178,7 +178,7 @@ const VoteButton = ({ ) : ( - Connect Wallet + Connect ) ) : ( diff --git a/src/pages/KyberDAO/Vote/index.tsx b/src/pages/KyberDAO/Vote/index.tsx index bb81c5d198..c0c671a9fd 100644 --- a/src/pages/KyberDAO/Vote/index.tsx +++ b/src/pages/KyberDAO/Vote/index.tsx @@ -421,7 +421,7 @@ export default function Vote() { ) ) : ( - Connect Your Wallet + Connect )} diff --git a/src/pages/MyEarnings/Placeholder.tsx b/src/pages/MyEarnings/Placeholder.tsx index 214ca89e7c..8bc296ddf7 100644 --- a/src/pages/MyEarnings/Placeholder.tsx +++ b/src/pages/MyEarnings/Placeholder.tsx @@ -65,7 +65,7 @@ const Placeholder = () => { }} onClick={toggleWalletModal} > - Connect Wallet + Connect diff --git a/src/pages/RemoveLiquidity/TokenPair.tsx b/src/pages/RemoveLiquidity/TokenPair.tsx index 9e0b968a48..f622f9924c 100644 --- a/src/pages/RemoveLiquidity/TokenPair.tsx +++ b/src/pages/RemoveLiquidity/TokenPair.tsx @@ -753,7 +753,7 @@ export default function TokenPair({
{!account ? ( - Connect Wallet + Connect ) : ( diff --git a/src/pages/RemoveLiquidity/ZapOut.tsx b/src/pages/RemoveLiquidity/ZapOut.tsx index 375f5626ef..ec772c21af 100644 --- a/src/pages/RemoveLiquidity/ZapOut.tsx +++ b/src/pages/RemoveLiquidity/ZapOut.tsx @@ -818,7 +818,7 @@ export default function ZapOut({
{!account ? ( - Connect Wallet + Connect ) : ( diff --git a/src/pages/SwapV2/index.tsx b/src/pages/SwapV2/index.tsx index 953b827e33..9d6cfe8143 100644 --- a/src/pages/SwapV2/index.tsx +++ b/src/pages/SwapV2/index.tsx @@ -541,7 +541,7 @@ export default function Swap() { {!account ? ( - Connect Wallet + Connect ) : showWrap ? ( diff --git a/src/pages/TrueSightV2/components/RequireConnectWallet.tsx b/src/pages/TrueSightV2/components/RequireConnectWallet.tsx index e5960c7c11..680264c389 100644 --- a/src/pages/TrueSightV2/components/RequireConnectWallet.tsx +++ b/src/pages/TrueSightV2/components/RequireConnectWallet.tsx @@ -78,7 +78,7 @@ export default function RequireConnectWalletWrapper({ height="36px" style={{ boxShadow: '0 2px 4px 2px #00000030' }} > - Connect Wallet + Connect ) diff --git a/src/state/burn/proamm/hooks.tsx b/src/state/burn/proamm/hooks.tsx index 656178b22e..a35f4914cc 100644 --- a/src/state/burn/proamm/hooks.tsx +++ b/src/state/burn/proamm/hooks.tsx @@ -122,7 +122,7 @@ export function useDerivedProAmmBurnInfo( pool && position ? pool.tickCurrent < position.tickLower || pool.tickCurrent >= position.tickUpper : false let error: ReactNode | undefined if (!account) { - error = Connect Wallet + error = Connect } // if (percent === 0) { // error = error ?? Enter a percent diff --git a/src/state/mint/proamm/hooks.tsx b/src/state/mint/proamm/hooks.tsx index ba592f1629..9f5eb988d4 100644 --- a/src/state/mint/proamm/hooks.tsx +++ b/src/state/mint/proamm/hooks.tsx @@ -515,7 +515,7 @@ export function useProAmmDerivedMintInfo( const currencyBalanceB = currencyBalances?.[Field.CURRENCY_B] const errorMessage: ReactNode | undefined = useMemo(() => { if (!account) { - return Connect Wallet + return Connect } if (poolState === PoolState.INVALID) { @@ -1068,7 +1068,7 @@ export function useProAmmDerivedAllMintInfo( const errorMessage: ReactNode | undefined = useMemo(() => { if (!account) { - return Connect Wallet + return Connect } if (poolState === PoolState.INVALID) { diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx index 08f0e240ab..7d37593e37 100644 --- a/src/state/user/hooks.tsx +++ b/src/state/user/hooks.tsx @@ -101,12 +101,14 @@ export function useUserLocaleManager(): [SupportedLocale | null, (newLocale: Sup return [locale, setLocale] } +// unused for now, but may be added again in the future. So we should keep it here. export function useIsAcceptedTerm(): [boolean, (isAcceptedTerm: boolean) => void] { const dispatch = useAppDispatch() const acceptedTermVersion = useSelector( state => state.user.acceptedTermVersion, ) + // eslint-disable-next-line @typescript-eslint/no-unused-vars const isAcceptedTerm = !!acceptedTermVersion && acceptedTermVersion === TERM_FILES_PATH.VERSION const setIsAcceptedTerm = useCallback( @@ -116,7 +118,8 @@ export function useIsAcceptedTerm(): [boolean, (isAcceptedTerm: boolean) => void [dispatch], ) - return [isAcceptedTerm, setIsAcceptedTerm] + // return [isAcceptedTerm, setIsAcceptedTerm] + return [true, setIsAcceptedTerm] } export function useDegenModeManager(): [boolean, () => void] { From 0e6362d9023d02a4ac14c12fb812ae84627532a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?= <33005392+nguyenhoaidanh@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:24:06 +0700 Subject: [PATCH 6/9] fix external link file (#2298) * fix external link file * rm unused --- src/components/Header/web3/WalletModal/index.tsx | 9 ++++----- src/components/Menu/index.tsx | 8 ++++---- src/pages/KyberDAO/KNCUtility/index.tsx | 13 ++++++------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/Header/web3/WalletModal/index.tsx b/src/components/Header/web3/WalletModal/index.tsx index 0986a50436..67e7834b65 100644 --- a/src/components/Header/web3/WalletModal/index.tsx +++ b/src/components/Header/web3/WalletModal/index.tsx @@ -32,7 +32,6 @@ import { useWalletModalToggle, } from 'state/application/hooks' import { useIsConnectingWallet } from 'state/authen/hooks' -import { ExternalLink } from 'theme' import { isEVMWallet, isOverriddenWallet, isSolanaWallet } from 'utils' import Option from './Option' @@ -317,13 +316,13 @@ export default function WalletModal() { By connecting a wallet, you accept{' '} - e.stopPropagation()}> + e.stopPropagation()}> KyberSwap‘s Terms of Use - {' '} + {' '} and consent to its{' '} - e.stopPropagation()}> + e.stopPropagation()}> Privacy Policy - + . Last updated: {dayjs(TERM_FILES_PATH.VERSION).format('DD MMM YYYY')} diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 44796e4579..87115a075a 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -473,7 +473,7 @@ export default function Menu() { )} - { toggle() @@ -482,10 +482,10 @@ export default function Menu() { > Terms - + - { toggle() @@ -494,7 +494,7 @@ export default function Menu() { > Privacy Policy - + These Terms and Conditions should be read in conjunction with the KyberSwap{' '} - Terms of Use, which lay out - the terms and conditions that apply to all KyberSwap activities. + Terms of Use, which lay out the terms and conditions + that apply to all KyberSwap activities. @@ -290,7 +290,7 @@ export default function KNCUtility() { By visiting KyberSwap and participating in the program, the User is deemed to have read, understood, and agreed to these Terms and Conditions and the KyberSwap{' '} - Terms of Use. + Terms of Use. @@ -308,10 +308,9 @@ export default function KNCUtility() { KyberSwap maintains the right, at its sole discretion, to take action or remove rewards against - the User who violates the KyberSwap{' '} - Terms of Use and/or violates, - cheats, or exploits the program, including but not limited to, any suspicious activities, or any - attempts to circumvent these Terms and Conditions. + the User who violates the KyberSwap Terms of Use{' '} + and/or violates, cheats, or exploits the program, including but not limited to, any suspicious + activities, or any attempts to circumvent these Terms and Conditions. From d557ea9bb92a6fa5112a8225a5ae6b04aba19b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?= <33005392+nguyenhoaidanh@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:44:24 +0700 Subject: [PATCH 7/9] update: my inbox watchlist kyberAI (#2297) --- .../InboxItemKyberAIWatchList.tsx | 4 ++-- .../NotificationCenter/KyberAIWatchlist.tsx | 2 +- .../TokenFilter/WatchlistSelect.tsx | 1 + .../pages/RegisterWhitelist/index.tsx | 20 +++++-------------- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx b/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx index c1e091c5f6..19f2a3f02a 100644 --- a/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx +++ b/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx @@ -19,7 +19,7 @@ const ItemWrapper = styled.div` gap: 8px; ` export const TokenInfo = ({ - showPrice = true, + showPrice = false, logoSize = '12px', token, }: { @@ -73,7 +73,7 @@ function InboxItemBridge({ } return ( - + diff --git a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx index c077f940ef..2bf1223fd5 100644 --- a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx +++ b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx @@ -53,7 +53,7 @@ export default function AnnouncementItem({ {!expand && minimalAssets.map((token, i) => ( - + {i === minimalAssets.length - 1 ? (minimalAssets.length < slice ? '' : ', ...') : ', '} ))} diff --git a/src/pages/TrueSightV2/components/TokenFilter/WatchlistSelect.tsx b/src/pages/TrueSightV2/components/TokenFilter/WatchlistSelect.tsx index 769c363bb6..f2d040cef9 100644 --- a/src/pages/TrueSightV2/components/TokenFilter/WatchlistSelect.tsx +++ b/src/pages/TrueSightV2/components/TokenFilter/WatchlistSelect.tsx @@ -16,6 +16,7 @@ const Divider = styled.div` ` const CustomOption = styled(Row)` + cursor: pointer; :hover { background-color: ${({ theme }) => theme.background}; } diff --git a/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx b/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx index b7dfeaeb18..28d4245171 100644 --- a/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx +++ b/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx @@ -10,7 +10,6 @@ import Column from 'components/Column' import DownloadWalletModal from 'components/DownloadWalletModal' import Row from 'components/Row' import { APP_PATHS } from 'constants/index' -import { useActiveWeb3React } from 'hooks' import useLogin from 'hooks/useLogin' import { MIXPANEL_TYPE, useMixpanelKyberAI } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' @@ -18,7 +17,7 @@ import SubscribeForm from 'pages/TrueSightV2/pages/RegisterWhitelist/SubscribeFo import WaitListForm from 'pages/TrueSightV2/pages/RegisterWhitelist/WaitListForm' import VerifyCodeModal from 'pages/Verify/VerifyCodeModal' import { ApplicationModal } from 'state/application/actions' -import { useOpenModal, useWalletModalToggle } from 'state/application/hooks' +import { useOpenModal } from 'state/application/hooks' import { useSessionInfo } from 'state/authen/hooks' import { useIsWhiteListKyberAI } from 'state/user/hooks' import { ButtonText } from 'theme' @@ -32,8 +31,6 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool const navigate = useNavigate() const theme = useTheme() const mixpanelHandler = useMixpanelKyberAI() - const { account } = useActiveWeb3React() - const toggleWalletModal = useWalletModalToggle() const { isLogin } = useSessionInfo() const { signIn } = useLogin() @@ -67,7 +64,7 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool <> @@ -99,11 +96,11 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool ) - if (!account) + if (!isLogin) return ( - - Sign in with wallet + signIn()}> + Sign-In to Continue Don't have a wallet? @@ -115,13 +112,6 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool ) - if (!isLogin) - return ( - signIn()}> - Sign-In to Continue - - ) - const btnGetStart = ( { From 985cfa2ef04eedc238bcbd9a274d6b2945677533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?= <33005392+nguyenhoaidanh@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:53:32 +0700 Subject: [PATCH 8/9] fix: allow external link with url: /pathname (#2299) --- .../Header/web3/WalletModal/index.tsx | 9 ++--- src/components/Menu/index.tsx | 8 ++--- src/pages/KyberDAO/KNCUtility/index.tsx | 13 +++---- src/pages/SwapV3/Tabs/LimitTab.tsx | 35 ++++++++++--------- src/theme/components.tsx | 20 ++++++++--- src/utils/redirect.ts | 20 ++++++----- 6 files changed, 62 insertions(+), 43 deletions(-) diff --git a/src/components/Header/web3/WalletModal/index.tsx b/src/components/Header/web3/WalletModal/index.tsx index 67e7834b65..0986a50436 100644 --- a/src/components/Header/web3/WalletModal/index.tsx +++ b/src/components/Header/web3/WalletModal/index.tsx @@ -32,6 +32,7 @@ import { useWalletModalToggle, } from 'state/application/hooks' import { useIsConnectingWallet } from 'state/authen/hooks' +import { ExternalLink } from 'theme' import { isEVMWallet, isOverriddenWallet, isSolanaWallet } from 'utils' import Option from './Option' @@ -316,13 +317,13 @@ export default function WalletModal() { By connecting a wallet, you accept{' '} - e.stopPropagation()}> + e.stopPropagation()}> KyberSwap‘s Terms of Use - {' '} + {' '} and consent to its{' '} - e.stopPropagation()}> + e.stopPropagation()}> Privacy Policy - + . Last updated: {dayjs(TERM_FILES_PATH.VERSION).format('DD MMM YYYY')} diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 87115a075a..44796e4579 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -473,7 +473,7 @@ export default function Menu() { )} - { toggle() @@ -482,10 +482,10 @@ export default function Menu() { > Terms - + - { toggle() @@ -494,7 +494,7 @@ export default function Menu() { > Privacy Policy - + These Terms and Conditions should be read in conjunction with the KyberSwap{' '} - Terms of Use, which lay out the terms and conditions - that apply to all KyberSwap activities. + Terms of Use, which lay out + the terms and conditions that apply to all KyberSwap activities. @@ -290,7 +290,7 @@ export default function KNCUtility() { By visiting KyberSwap and participating in the program, the User is deemed to have read, understood, and agreed to these Terms and Conditions and the KyberSwap{' '} - Terms of Use. + Terms of Use. @@ -308,9 +308,10 @@ export default function KNCUtility() { KyberSwap maintains the right, at its sole discretion, to take action or remove rewards against - the User who violates the KyberSwap Terms of Use{' '} - and/or violates, cheats, or exploits the program, including but not limited to, any suspicious - activities, or any attempts to circumvent these Terms and Conditions. + the User who violates the KyberSwap{' '} + Terms of Use and/or violates, + cheats, or exploits the program, including but not limited to, any suspicious activities, or any + attempts to circumvent these Terms and Conditions. diff --git a/src/pages/SwapV3/Tabs/LimitTab.tsx b/src/pages/SwapV3/Tabs/LimitTab.tsx index c46bd4b04c..59fe4c6cce 100644 --- a/src/pages/SwapV3/Tabs/LimitTab.tsx +++ b/src/pages/SwapV3/Tabs/LimitTab.tsx @@ -1,7 +1,6 @@ import { Trans } from '@lingui/macro' import { rgba } from 'polished' import { useLocation } from 'react-router-dom' -import { Text } from 'rebass' import { useGetNumberOfInsufficientFundOrdersQuery } from 'services/limitOrder' import styled from 'styled-components' @@ -45,22 +44,24 @@ export default function LimitTab({ onClick }: Props) { } return ( - - - Limit{' '} - {numberOfInsufficientFundOrders ? ( - - You have {numberOfInsufficientFundOrders} active orders that don't have sufficient funds - - } - > - {numberOfInsufficientFundOrders} - - ) : null} - + + Limit{' '} + {!!numberOfInsufficientFundOrders && ( + You have {numberOfInsufficientFundOrders} active orders that don't have sufficient funds + } + > + {numberOfInsufficientFundOrders} + + )} ) } diff --git a/src/theme/components.tsx b/src/theme/components.tsx index 5fb445e175..7afcfc381a 100644 --- a/src/theme/components.tsx +++ b/src/theme/components.tsx @@ -193,7 +193,7 @@ export function ExternalLink({ }: Omit, 'as' | 'ref'> & { href: string }) { const handleClick = useCallback( (event: React.MouseEvent) => { - onClick && onClick(event) + onClick?.(event) // don't prevent default, don't redirect if it's a new tab if (target === '_blank' || event.ctrlKey || event.metaKey) { } else { @@ -203,7 +203,13 @@ export function ExternalLink({ [target, onClick], ) return ( - + ) } @@ -221,13 +227,19 @@ export function ExternalLinkIcon({ console.debug('Fired outbound link event', href) } else { event.preventDefault() - navigateToUrl(href, false) + navigateToUrl(href, { _dangerousSkipCheckWhitelist: true, allowRelativePath: true }) } }, [href, target], ) return ( - + ) diff --git a/src/utils/redirect.ts b/src/utils/redirect.ts index 4dec7f623a..d472a54b66 100644 --- a/src/utils/redirect.ts +++ b/src/utils/redirect.ts @@ -6,15 +6,19 @@ import { useActiveWeb3React } from 'hooks' import { useChangeNetwork } from 'hooks/web3/useChangeNetwork' const whiteListDomains = [/https:\/\/(.+?\.)?kyberswap\.com$/, /https:\/\/(.+)\.kyberengineering\.io$/] -export const validateRedirectURL = (url: string | undefined, whitelistKyberSwap = true) => { + +type Options = { _dangerousSkipCheckWhitelist?: boolean; allowRelativePath?: boolean } +export const validateRedirectURL = ( + url: string | undefined, + { _dangerousSkipCheckWhitelist = false, allowRelativePath = false }: Options = {}, +) => { try { - if (!url) throw new Error() - const newUrl = new URL(url) // valid url + if (!url || url.endsWith('.js')) throw new Error() + const newUrl = allowRelativePath && url.startsWith('/') ? new URL(`${window.location.origin}${url}`) : new URL(url) if ( - url.endsWith('.js') || newUrl.pathname.endsWith('.js') || !['https:', 'http:'].includes(newUrl.protocol) || - (whitelistKyberSwap && !whiteListDomains.some(regex => newUrl.origin.match(regex))) + (!_dangerousSkipCheckWhitelist && !whiteListDomains.some(regex => newUrl.origin.match(regex))) ) { throw new Error() } @@ -24,8 +28,8 @@ export const validateRedirectURL = (url: string | undefined, whitelistKyberSwap } } -export const navigateToUrl = (url: string | undefined, whitelistKyberSwap = true) => { - const urlFormatted = validateRedirectURL(url, whitelistKyberSwap) +export const navigateToUrl = (url: string | undefined, options?: Options) => { + const urlFormatted = validateRedirectURL(url, options) if (urlFormatted) window.location.href = urlFormatted } @@ -46,7 +50,7 @@ export const useNavigateToUrl = () => { return } const { pathname, host, search } = new URL(actionURL) - if (!validateRedirectURL(actionURL, false)) return + if (!validateRedirectURL(actionURL, { _dangerousSkipCheckWhitelist: true })) return if (window.location.host === host) { navigate(`${pathname}${search}`) } else { From 10c0d27f88816832012d00d5c3a72e43f0a13006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?= <33005392+nguyenhoaidanh@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:58:33 +0700 Subject: [PATCH 9/9] refactor: rtk query campagin (#2260) --- src/components/Announcement/helper.ts | 20 - src/components/Announcement/index.tsx | 3 +- src/components/ProtectedRoute.tsx | 14 +- .../YourCampaignTransactionsModal/index.tsx | 43 +- .../swapv2/LimitOrder/ListOrder/index.tsx | 6 +- src/constants/index.ts | 14 +- src/hooks/useInvalidateTags.ts | 28 ++ src/pages/Campaign/CampaignContent.tsx | 461 ++++++++---------- src/pages/Campaign/CampaignListAndSearch.tsx | 4 +- src/pages/Campaign/LeaderboardLayout.tsx | 60 ++- .../Campaign/ModalRegisterCampaignCaptcha.tsx | 25 +- src/pages/Campaign/ModalSelectCampaign.tsx | 2 +- src/pages/Campaign/index.tsx | 324 +----------- .../PrivateAnnouncement.tsx | 2 +- src/services/campaign.ts | 260 ++++++++++ src/state/campaigns/actions.ts | 15 +- src/state/campaigns/hooks.ts | 35 -- src/state/campaigns/reducer.ts | 46 +- src/state/index.ts | 23 +- 19 files changed, 614 insertions(+), 771 deletions(-) create mode 100644 src/hooks/useInvalidateTags.ts create mode 100644 src/services/campaign.ts diff --git a/src/components/Announcement/helper.ts b/src/components/Announcement/helper.ts index 6dfcf4dfca..0ba391456d 100644 --- a/src/components/Announcement/helper.ts +++ b/src/components/Announcement/helper.ts @@ -1,10 +1,7 @@ import { ChainId } from '@kyberswap/ks-sdk-core' -import { useCallback } from 'react' -import AnnouncementApi from 'services/announcement' import { AnnouncementTemplatePopup, PopupContentAnnouncement, PopupItemType } from 'components/Announcement/type' import { TIMES_IN_SECS } from 'constants/index' -import { useAppDispatch } from 'state/hooks' const LsKey = 'ack-announcements' export const getAnnouncementsAckMap = () => JSON.parse(localStorage[LsKey] || '{}') @@ -42,20 +39,3 @@ export const isPopupCanShow = ( const isExpired = Date.now() < startAt * 1000 || Date.now() > endAt * 1000 return !isRead && !isExpired && isRightChain && isOwn } - -export const useInvalidateTags = (reducerPath: string) => { - const dispatch = useAppDispatch() - return useCallback( - (tag: string | string[]) => { - dispatch({ - type: `${reducerPath}/invalidateTags`, - payload: Array.isArray(tag) ? tag : [tag], - }) - }, - [dispatch, reducerPath], - ) -} - -export const useInvalidateTagAnnouncement = () => { - return useInvalidateTags(AnnouncementApi.reducerPath) -} diff --git a/src/components/Announcement/index.tsx b/src/components/Announcement/index.tsx index 3abf63be97..dd56ff5b27 100644 --- a/src/components/Announcement/index.tsx +++ b/src/components/Announcement/index.tsx @@ -10,13 +10,14 @@ import styled, { css } from 'styled-components' import AnnouncementView, { Tab } from 'components/Announcement/AnnoucementView' import DetailAnnouncementPopup from 'components/Announcement/Popups/DetailAnnouncementPopup' -import { formatNumberOfUnread, useInvalidateTagAnnouncement } from 'components/Announcement/helper' +import { formatNumberOfUnread } from 'components/Announcement/helper' import { Announcement, PrivateAnnouncement } from 'components/Announcement/type' import NotificationIcon from 'components/Icons/NotificationIcon' import MenuFlyout from 'components/MenuFlyout' import Modal from 'components/Modal' import { RTK_QUERY_TAGS } from 'constants/index' import useInterval from 'hooks/useInterval' +import { useInvalidateTagAnnouncement } from 'hooks/useInvalidateTags' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import { ApplicationModal } from 'state/application/actions' import { useDetailAnnouncement, useModalOpen, useToggleNotificationCenter } from 'state/application/hooks' diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx index ff131bba05..326c38c5ed 100644 --- a/src/components/ProtectedRoute.tsx +++ b/src/components/ProtectedRoute.tsx @@ -3,9 +3,8 @@ import { Navigate } from 'react-router-dom' import LocalLoader from 'components/LocalLoader' import { RTK_QUERY_TAGS } from 'constants/index' -import kyberAIapi from 'pages/TrueSightV2/hooks/useKyberAIData' +import { useInvalidateTagKyberAi } from 'hooks/useInvalidateTags' import { useSessionInfo } from 'state/authen/hooks' -import { useAppDispatch } from 'state/hooks' import { useIsWhiteListKyberAI } from 'state/user/hooks' type Props = { @@ -33,20 +32,15 @@ export const ProtectedRouteKyberAI = ({ const { userInfo } = useSessionInfo() const loadedPage = useRef(false) const canAccessPage = isWhiteList || waitUtilAuthenEndOnly - const dispatch = useAppDispatch() + const invalidateTags = useInvalidateTagKyberAi() useEffect(() => { // change account sign in => refresh participant info try { refetch() - dispatch( - kyberAIapi.util.invalidateTags([ - RTK_QUERY_TAGS.GET_WATCHLIST_TOKENS_KYBER_AI, - RTK_QUERY_TAGS.GET_WATCHLIST_INFO_KYBER_AI, - ]), - ) + invalidateTags([RTK_QUERY_TAGS.GET_WATCHLIST_TOKENS_KYBER_AI, RTK_QUERY_TAGS.GET_WATCHLIST_INFO_KYBER_AI]) } catch (error) {} - }, [userInfo?.identityId, refetch, dispatch]) + }, [userInfo?.identityId, refetch, invalidateTags]) if (loading && !loadedPage.current) return if (!canAccessPage) return diff --git a/src/components/YourCampaignTransactionsModal/index.tsx b/src/components/YourCampaignTransactionsModal/index.tsx index 52e151a255..68b3bc1962 100644 --- a/src/components/YourCampaignTransactionsModal/index.tsx +++ b/src/components/YourCampaignTransactionsModal/index.tsx @@ -5,11 +5,11 @@ import { CheckCircle, Copy, ExternalLink, Info, X } from 'react-feather' import { useSelector } from 'react-redux' import { useMedia } from 'react-use' import { Flex, Text } from 'rebass' +import { useGetTxsCampaignQuery } from 'services/campaign' import styled, { css } from 'styled-components' -import useSWR from 'swr' import Modal from 'components/Modal' -import { CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE, SWR_KEYS } from 'constants/index' +import { CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE } from 'constants/index' import { NETWORKS_INFO } from 'constants/networks' import { useActiveWeb3React } from 'hooks' import useCopyClipboard from 'hooks/useCopyClipboard' @@ -17,7 +17,6 @@ import useTheme from 'hooks/useTheme' import { AppState } from 'state' import { ApplicationModal } from 'state/application/actions' import { useModalOpen, useToggleYourCampaignTransactionsModal } from 'state/application/hooks' -import { CampaignProofData } from 'state/campaigns/actions' import { getEtherscanLink } from 'utils' import getShortenAddress from 'utils/getShortenAddress' @@ -32,37 +31,15 @@ export default function YourCampaignTransactionsModal() { const above768 = useMedia('(min-width: 768px)') const selectedCampaign = useSelector((state: AppState) => state.campaigns.selectedCampaign) - const { data: userCampaignTransactions } = useSWR( - account && selectedCampaign - ? SWR_KEYS.getCampaignTransactions( - selectedCampaign.id, - CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE, - CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE * (currentPage - 1), - account, - ) - : null, - async (url: string) => { - try { - const response = await fetch(url) - if (response.ok) { - const data = await response.json() - if (data && Array.isArray(data.data) && data.data.length) { - return data.data.map( - (item: any): CampaignProofData => ({ - id: item.id, - chainId: parseInt(item.chainId), - utcTimestamp: new Date(item.time).getTime(), - txPoint: item.txPoint, - txHash: item.tx, - }), - ) - } - } - return [] - } catch (err) { - console.error(err) - } + + const { data: userCampaignTransactions } = useGetTxsCampaignQuery( + { + campaignId: selectedCampaign?.id || 0, + limit: CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE, + offset: CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE * (currentPage - 1), + userAddress: account ?? '', }, + { skip: !selectedCampaign?.id }, ) const [isCopied, setCopied] = useCopyClipboard() diff --git a/src/components/swapv2/LimitOrder/ListOrder/index.tsx b/src/components/swapv2/LimitOrder/ListOrder/index.tsx index e21d7fc949..fb99eed161 100644 --- a/src/components/swapv2/LimitOrder/ListOrder/index.tsx +++ b/src/components/swapv2/LimitOrder/ListOrder/index.tsx @@ -6,11 +6,10 @@ import { Trash } from 'react-feather' import { useNavigate } from 'react-router-dom' import { useMedia } from 'react-use' import { Flex, Text } from 'rebass' -import limitOrderApi, { useGetListOrdersQuery } from 'services/limitOrder' +import { useGetListOrdersQuery } from 'services/limitOrder' import styled from 'styled-components' import { ReactComponent as NoDataIcon } from 'assets/svg/no-data.svg' -import { useInvalidateTags } from 'components/Announcement/helper' import { ButtonLight } from 'components/Button' import Column from 'components/Column' import LocalLoader from 'components/LocalLoader' @@ -22,6 +21,7 @@ import SubscribeNotificationButton from 'components/SubscribeButton' import useRequestCancelOrder from 'components/swapv2/LimitOrder/ListOrder/useRequestCancelOrder' import { EMPTY_ARRAY, RTK_QUERY_TAGS, TRANSACTION_STATE_DEFAULT } from 'constants/index' import { useActiveWeb3React } from 'hooks' +import { useInvalidateTagLimitOrder } from 'hooks/useInvalidateTags' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useParsedQueryString from 'hooks/useParsedQueryString' import useShowLoadingAtLeastTime from 'hooks/useShowLoadingAtLeastTime' @@ -203,7 +203,7 @@ export default function ListLimitOrder() { onReset() }, [chainId, orderType]) - const invalidateTag = useInvalidateTags(limitOrderApi.reducerPath) + const invalidateTag = useInvalidateTagLimitOrder() const refetchOrders = useCallback(() => { invalidateTag(RTK_QUERY_TAGS.GET_LIST_ORDERS) }, [invalidateTag]) diff --git a/src/constants/index.ts b/src/constants/index.ts index 3959404bda..e181ef45c2 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid' import { TransactionFlowState } from 'types/TransactionFlowState' -import { CAMPAIGN_BASE_URL as CAMPAIGN_BASE_DOMAIN } from './env' +import { CAMPAIGN_BASE_URL } from './env' import * as ENV from './env' import { EVM_MAINNET_NETWORKS, EVM_NETWORK, NETWORKS_INFO, SUPPORTED_NETWORKS, isEVM } from './networks' @@ -186,16 +186,10 @@ export const CAMPAIGN_YOUR_TRANSACTIONS_ITEM_PER_PAGE = 10000 export const ELASTIC_BASE_FEE_UNIT = 100_000 export const KYBERSWAP_SOURCE = '{"source":"kyberswap"}' -export const CAMPAIGN_BASE_URL = `${CAMPAIGN_BASE_DOMAIN}/api/v1/campaigns` export const SWR_KEYS = { - getGrantProgramLeaderBoard: (id: number | string) => `${CAMPAIGN_BASE_DOMAIN}/api/v1/competitions/${id}/leaderboard`, - getListGrantPrograms: `${CAMPAIGN_BASE_DOMAIN}/api/v1/competitions`, - getGrantProgram: (id: number | string) => `${CAMPAIGN_BASE_DOMAIN}/api/v1/competitions/${id}`, - getListCampaign: CAMPAIGN_BASE_URL, - getLeaderboard: (id: number) => CAMPAIGN_BASE_URL + '/' + id + '/leaderboard', - getLuckyWinners: (id: number) => CAMPAIGN_BASE_URL + '/' + id + '/lucky-winners', - getCampaignTransactions: (campaignId: number, limit: number, offset: number, account: string) => - `${CAMPAIGN_BASE_URL}/${campaignId}/proofs?limit=${limit}&offset=${offset}&userAddress=${account}`, + getGrantProgramLeaderBoard: (id: number | string) => `${CAMPAIGN_BASE_URL}/api/v1/competitions/${id}/leaderboard`, + getListGrantPrograms: `${CAMPAIGN_BASE_URL}/api/v1/competitions`, + getGrantProgram: (id: number | string) => `${CAMPAIGN_BASE_URL}/api/v1/competitions/${id}`, } // https://www.nasdaq.com/glossary/b/bip diff --git a/src/hooks/useInvalidateTags.ts b/src/hooks/useInvalidateTags.ts new file mode 100644 index 0000000000..3a1c9cb3e1 --- /dev/null +++ b/src/hooks/useInvalidateTags.ts @@ -0,0 +1,28 @@ +import { useCallback } from 'react' +import announcementApi from 'services/announcement' +import limitOrderApi from 'services/limitOrder' + +import kyberAIApi from 'pages/TrueSightV2/hooks/useKyberAIData' +import { useAppDispatch } from 'state/hooks' + +const useInvalidateTags = (api: any) => { + const dispatch = useAppDispatch() + return useCallback( + (tag: string | string[]) => { + dispatch(api.util.invalidateTags(Array.isArray(tag) ? tag : [tag])) + }, + [dispatch, api], + ) +} + +export const useInvalidateTagAnnouncement = () => { + return useInvalidateTags(announcementApi) +} + +export const useInvalidateTagKyberAi = () => { + return useInvalidateTags(kyberAIApi) +} + +export const useInvalidateTagLimitOrder = () => { + return useInvalidateTags(limitOrderApi) +} diff --git a/src/pages/Campaign/CampaignContent.tsx b/src/pages/Campaign/CampaignContent.tsx index 27a96703e6..c43b010e8a 100644 --- a/src/pages/Campaign/CampaignContent.tsx +++ b/src/pages/Campaign/CampaignContent.tsx @@ -1,6 +1,6 @@ import { Trans, t } from '@lingui/macro' import dayjs from 'dayjs' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { isMobile } from 'react-device-detect' import { BarChart, ChevronDown, Clock, Share2, Star, Users } from 'react-feather' import { useSelector } from 'react-redux' @@ -8,7 +8,6 @@ import { useNavigate } from 'react-router-dom' import { useMedia } from 'react-use' import { Flex, Text } from 'rebass' import styled, { css } from 'styled-components' -import { useSWRConfig } from 'swr' import { ButtonEmpty, ButtonLight } from 'components/Button' import Divider from 'components/Divider' @@ -18,7 +17,6 @@ import ProgressBar from 'components/ProgressBar' import ShareModal from 'components/ShareModal' import { MouseoverTooltip, TextDashed } from 'components/Tooltip' import YourCampaignTransactionsModal from 'components/YourCampaignTransactionsModal' -import { SWR_KEYS } from 'constants/index' import { useActiveWeb3React, useWeb3React } from 'hooks' import useInterval from 'hooks/useInterval' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' @@ -57,6 +55,8 @@ import oembed2iframe from 'utils/oembed2iframe' import ModalSelectCampaign from './ModalSelectCampaign' +export const MINUTE_TO_REFRESH = 5 * 60 + const LoaderParagraphs = () => ( <> @@ -176,7 +176,7 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp const { mixpanelHandler } = useMixpanel() - const above768 = useMedia(`(min-width: ${MEDIA_WIDTHS.upToSmall}px)`) + const upToSmall = useMedia(`(min-width: ${MEDIA_WIDTHS.upToSmall}px)`) const campaignDetailImageRef = useRef(null) const [campaignDetailMediaLoadedMap, setCampaignDetailMediaLoadedMap] = useState<{ [id: string]: boolean }>({}) @@ -313,9 +313,12 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp const toggleSelectCampaignModal = useSelectCampaignModalToggle() const navigate = useNavigate() - const onSelectCampaign = (campaign: CampaignData) => { - navigate(getSlugUrlCampaign(campaign.id, campaign.name)) - } + const onSelectCampaign = useCallback( + (campaign: CampaignData) => { + navigate(getSlugUrlCampaign(campaign.id, campaign.name)) + }, + [navigate], + ) const now = Date.now() @@ -323,13 +326,9 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp loadingCampaignData, loadingCampaignDataError, data: campaigns, - selectedCampaignLeaderboardPageNumber, - selectedCampaignLeaderboardLookupAddress, } = useSelector((state: AppState) => state.campaigns) - const MINUTE_TO_REFRESH = 5 - const [campaignsRefreshIn, setCampaignsRefreshIn] = useState(MINUTE_TO_REFRESH * 60) - const { mutate } = useSWRConfig() + const [campaignsRefreshIn, setCampaignsRefreshIn] = useState(MINUTE_TO_REFRESH) const dispatch = useAppDispatch() useInterval( () => { @@ -339,10 +338,7 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp setCampaignData({ campaigns: campaigns.map(campaign => { if (campaign.id === selectedCampaign.id) { - return { - ...campaign, - status, - } + return { ...campaign, status } } return campaign }), @@ -364,36 +360,12 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp ) { updateCampaignStatus(CampaignStatus.ENDED) } - setCampaignsRefreshIn(prev => { - if (prev === 0) { - return MINUTE_TO_REFRESH * 60 - } - return prev - 1 - }) + setCampaignsRefreshIn(prev => (prev === 0 ? MINUTE_TO_REFRESH : prev - 1)) }, selectedCampaign?.campaignState === CampaignState.CampaignStateReady ? 1000 : null, true, ) - useEffect(() => { - if (campaignsRefreshIn === 0 && selectedCampaign) { - mutate([ - selectedCampaign, - SWR_KEYS.getLeaderboard(selectedCampaign.id), - selectedCampaignLeaderboardPageNumber, - selectedCampaignLeaderboardLookupAddress, - account, - ]) - } - }, [ - mutate, - campaignsRefreshIn, - selectedCampaign, - selectedCampaignLeaderboardPageNumber, - selectedCampaignLeaderboardLookupAddress, - account, - ]) - if (campaigns.length === 0 && loadingCampaignData) { return } @@ -409,218 +381,214 @@ export default function Campaign({ refreshListCampaign, ...props }: CampaignProp return ( <> - - - - - - - - - - Campaigns - - - - - - - - - - - - - { - setTimeout(() => { - if (selectedCampaign) - setCampaignDetailMediaLoadedMap(prev => ({ ...prev, [selectedCampaign.id]: true })) - }, 500) - }} - onError={() => { + + + + + + + + + Campaigns + + + + + + + + + + + + + { + setTimeout(() => { if (selectedCampaign) setCampaignDetailMediaLoadedMap(prev => ({ ...prev, [selectedCampaign.id]: true })) - if (campaignDetailImageRef && campaignDetailImageRef.current) { - campaignDetailImageRef.current.style.display = 'none' - } - }} + }, 500) + }} + onError={() => { + if (selectedCampaign) + setCampaignDetailMediaLoadedMap(prev => ({ ...prev, [selectedCampaign.id]: true })) + if (campaignDetailImageRef && campaignDetailImageRef.current) { + campaignDetailImageRef.current.style.display = 'none' + } + }} + /> + + + + {selectedCampaign?.name} + + + + + + + + mixpanelHandler(MIXPANEL_TYPE.CAMPAIGN_SHARE_TRADING_CONTEST_CLICKED, { + campaign_name: selectedCampaign?.name, + }) + } /> - - - - {selectedCampaign?.name} + + + + + + {selectedCampaign?.status === CampaignStatus.UPCOMING + ? t`Starting In` + : isOngoing + ? t`Ending In` + : t`Ended On`} - - - - - - - mixpanelHandler(MIXPANEL_TYPE.CAMPAIGN_SHARE_TRADING_CONTEST_CLICKED, { - campaign_name: selectedCampaign?.name, - }) - } - /> - - - - - - {selectedCampaign?.status === CampaignStatus.UPCOMING - ? t`Starting In` - : isOngoing - ? t`Ending In` - : t`Ended On`} + + {isSelectedCampaignMediaLoaded ? ( + <> + {selectedCampaign.status === CampaignStatus.UPCOMING && ( + + + {selectedCampaign + ? getFormattedTimeFromSecond((selectedCampaign.startTime - now) / 1000) + : '--'} + + + )} + {selectedCampaign.status === CampaignStatus.ONGOING && ( + + + {selectedCampaign ? getFormattedTimeFromSecond((selectedCampaign.endTime - now) / 1000) : '--'} + + + )} + {selectedCampaign.status === CampaignStatus.ENDED && ( + + {dayjs(selectedCampaign.endTime).format('YYYY-MM-DD HH:mm')} + + )} + + ) : ( + + )} + + + + Participants + + {!isMobile && } + {isSelectedCampaignMediaLoaded ? ( + + {selectedCampaignLeaderboard?.totalParticipants + ? formatNumberWithPrecisionRange(selectedCampaignLeaderboard.totalParticipants, 0, 0) + : '--'} - - {isSelectedCampaignMediaLoaded ? ( - <> - {selectedCampaign.status === CampaignStatus.UPCOMING && ( - - - {selectedCampaign - ? getFormattedTimeFromSecond((selectedCampaign.startTime - now) / 1000) - : '--'} - - - )} - {selectedCampaign.status === CampaignStatus.ONGOING && ( - - - {selectedCampaign - ? getFormattedTimeFromSecond((selectedCampaign.endTime - now) / 1000) - : '--'} - - - )} - {selectedCampaign.status === CampaignStatus.ENDED && ( - - {dayjs(selectedCampaign.endTime).format('YYYY-MM-DD HH:mm')} + ) : ( + + )} + + + + Your Rank + {isMobile && } + + {!isMobile && } + {isSelectedCampaignMediaLoaded ? ( + account ? ( + + + + {selectedCampaign?.userInfo?.rankNo + ? formatNumberWithPrecisionRange(selectedCampaign?.userInfo?.rankNo, 0, 2) + : '--'} - )} - - ) : ( - - )} - - - - Participants - - {!isMobile && } - {isSelectedCampaignMediaLoaded ? ( - - {selectedCampaignLeaderboard?.totalParticipants - ? formatNumberWithPrecisionRange(selectedCampaignLeaderboard.totalParticipants, 0, 0) - : '--'} - - ) : ( - - )} - - - - Your Rank - {isMobile && } - - {!isMobile && } - {isSelectedCampaignMediaLoaded ? ( - account ? ( - - - - {selectedCampaign?.userInfo?.rankNo - ? formatNumberWithPrecisionRange(selectedCampaign?.userInfo?.rankNo, 0, 2) - : '--'} - - {!isMobile && } - - - {above768 ? Your Transactions : History} - + {!isMobile && } - ) : ( - - Connect - - ) + + {upToSmall ? Your Transactions : History} + + ) : ( - - )} - - - - - setActiveTab(CampaignTab.HOW_TO_WIN)} - > - How to win - - setActiveTab(CampaignTab.REWARDS)} - > - Rewards - + + Connect + + ) + ) : ( + + )} + + + + + setActiveTab(CampaignTab.HOW_TO_WIN)} + > + How to win + + setActiveTab(CampaignTab.REWARDS)} + > + Rewards + + setActiveTab(CampaignTab.LEADERBOARD)} + > + Leaderboard + + {selectedCampaign && selectedCampaign.campaignState === CampaignState.CampaignStateDistributedRewards && ( setActiveTab(CampaignTab.LEADERBOARD)} + active={activeTab === CampaignTab.LUCKY_WINNER} + onClick={() => setActiveTab(CampaignTab.LUCKY_WINNER)} > - Leaderboard + Lucky Winners - {selectedCampaign && selectedCampaign.campaignState === CampaignState.CampaignStateDistributedRewards && ( - setActiveTab(CampaignTab.LUCKY_WINNER)} - > - Lucky Winners - - )} - + )} + - - {activeTab === CampaignTab.HOW_TO_WIN && } - {activeTab === CampaignTab.REWARDS && } - {activeTab === CampaignTab.LEADERBOARD && ( - - )} - {activeTab === CampaignTab.LUCKY_WINNER && ( - - )} - - - + + {activeTab === CampaignTab.HOW_TO_WIN && } + {activeTab === CampaignTab.REWARDS && } + {activeTab === CampaignTab.LEADERBOARD && ( + + )} + {activeTab === CampaignTab.LUCKY_WINNER && ( + + )} + + @@ -753,7 +721,9 @@ const PageWrapper = styled.div` padding: 32px 24px 50px; width: 100%; max-width: 1500px; - + display: flex; + gap: 24px; + min-height: calc(100vh - 84.34px - 24px - 24px - 62px); ${({ theme }) => theme.mediaWidth.upToSmall` ${css` padding: 24px 16px 100px; @@ -761,13 +731,6 @@ const PageWrapper = styled.div` `} ` -const CampaignContainer = styled.div` - display: flex; - gap: 24px; - min-height: calc(100vh - 84.34px - 24px - 24px - 62px); - overflow: auto; -` - const CampaignDetail = styled.div` flex: 2; overflow: auto; diff --git a/src/pages/Campaign/CampaignListAndSearch.tsx b/src/pages/Campaign/CampaignListAndSearch.tsx index c75ca9c18a..7457b1ccf4 100644 --- a/src/pages/Campaign/CampaignListAndSearch.tsx +++ b/src/pages/Campaign/CampaignListAndSearch.tsx @@ -1,5 +1,5 @@ import { Trans, t } from '@lingui/macro' -import { useCallback, useEffect, useRef, useState } from 'react' +import { memo, useCallback, useEffect, useRef, useState } from 'react' import { useSelector } from 'react-redux' import AutoSizer from 'react-virtualized-auto-sizer' import { VariableSizeList } from 'react-window' @@ -172,4 +172,4 @@ const CampaignListAndSearch = ({ ) } -export default CampaignListAndSearch +export default memo(CampaignListAndSearch) diff --git a/src/pages/Campaign/LeaderboardLayout.tsx b/src/pages/Campaign/LeaderboardLayout.tsx index 80ff77ca21..5e7b669170 100644 --- a/src/pages/Campaign/LeaderboardLayout.tsx +++ b/src/pages/Campaign/LeaderboardLayout.tsx @@ -1,11 +1,12 @@ import { Trans, t } from '@lingui/macro' import dayjs from 'dayjs' import { rgba } from 'polished' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' import { Clock } from 'react-feather' import { useSelector } from 'react-redux' import { useMedia, useSize } from 'react-use' import { Flex, Text } from 'rebass' +import { useGetLuckyWinnersQuery } from 'services/campaign' import styled, { css } from 'styled-components' import Bronze from 'assets/svg/bronze_icon.svg' @@ -14,15 +15,13 @@ import Silver from 'assets/svg/silver_icon.svg' import InfoHelper from 'components/InfoHelper' import Pagination from 'components/Pagination' import Search, { Container as SearchContainer, Wrapper as SearchWrapper } from 'components/Search' -import { BIG_INT_ZERO, CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, DEFAULT_SIGNIFICANT } from 'constants/index' +import { BIG_INT_ZERO, CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, DEFAULT_SIGNIFICANT, EMPTY_ARRAY } from 'constants/index' import useTheme from 'hooks/useTheme' import { AppState } from 'state' import { CampaignState, CampaignStatus, RewardRandom } from 'state/campaigns/actions' import { useSelectedCampaignLeaderboardLookupAddressManager, useSelectedCampaignLeaderboardPageNumberManager, - useSelectedCampaignLuckyWinnerPageNumber, - useSelectedCampaignLuckyWinnersLookupAddressManager, } from 'state/campaigns/hooks' import { formatNumberWithPrecisionRange } from 'utils' import getShortenAddress from 'utils/getShortenAddress' @@ -48,35 +47,44 @@ export default function LeaderboardLayout({ )) - const { selectedCampaignLeaderboard, selectedCampaignLuckyWinners, selectedCampaign } = useSelector( - (state: AppState) => state.campaigns, - ) - - const [currentPage, setCurrentPage] = useSelectedCampaignLeaderboardPageNumberManager() - const [currentPageLuckyWinner, setCurrentPageLuckyWinner] = useSelectedCampaignLuckyWinnerPageNumber() + const { selectedCampaignLeaderboard, selectedCampaign } = useSelector((state: AppState) => state.campaigns) const [leaderboardSearchValue, setLeaderboardSearchValue] = useSelectedCampaignLeaderboardLookupAddressManager() - const [luckyWinnersSearchValue, setLuckyWinnersSearchValue] = useSelectedCampaignLuckyWinnersLookupAddressManager() + + const [luckyWinnersSearchValue, setLuckyWinnersSearchValue] = useState('') + const [searchValue, setSearchValue] = type === 'leaderboard' ? [leaderboardSearchValue, setLeaderboardSearchValue] : [luckyWinnersSearchValue, setLuckyWinnersSearchValue] + const [currentPageLuckyWinner, setCurrentPageLuckyWinner] = useState(0) + const { currentData: dataLuckWinners } = useGetLuckyWinnersQuery( + { + pageSize: CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, + pageNumber: currentPageLuckyWinner, + lookupAddress: luckyWinnersSearchValue, + campaignId: selectedCampaign?.id || 0, + }, + { skip: !selectedCampaign?.id }, + ) + + const luckyWinners = + (selectedCampaign?.campaignState === CampaignState.CampaignStateReady ? EMPTY_ARRAY : dataLuckWinners) || + EMPTY_ARRAY + + const [currentPage, setCurrentPage] = useSelectedCampaignLeaderboardPageNumberManager() let totalItems = 0 - if (type === 'leaderboard') { - if (selectedCampaignLeaderboard) { - totalItems = leaderboardSearchValue ? 1 : selectedCampaignLeaderboard.totalParticipants - } + if (type === 'leaderboard' && selectedCampaignLeaderboard) { + totalItems = leaderboardSearchValue ? 1 : selectedCampaignLeaderboard.totalParticipants } - if (type === 'lucky_winner') { - if (selectedCampaign && selectedCampaignLeaderboard) { - const randomRewards = selectedCampaign.rewardDistribution.filter(reward => reward.type === 'Random') - const totalRandomRewardItems = randomRewards.reduce( - (acc, reward) => acc + ((reward as RewardRandom).nWinners ?? 0), - 0, - ) - - totalItems = searchValue ? 1 : Math.min(totalRandomRewardItems, selectedCampaignLeaderboard.totalParticipants) - } + if (type === 'lucky_winner' && selectedCampaign && selectedCampaignLeaderboard) { + const randomRewards = selectedCampaign.rewardDistribution.filter(reward => reward.type === 'Random') + const totalRandomRewardItems = randomRewards.reduce( + (acc, reward) => acc + ((reward as RewardRandom).nWinners ?? 0), + 0, + ) + + totalItems = searchValue ? 1 : Math.min(totalRandomRewardItems, selectedCampaignLeaderboard.totalParticipants) } const refreshInMinute = Math.floor(refreshIn / 60) @@ -143,7 +151,7 @@ export default function LeaderboardLayout({ ) }) - const luckyWinnersTableBody = selectedCampaignLuckyWinners.map((luckyWinner, index) => { + const luckyWinnersTableBody = luckyWinners.map((luckyWinner, index) => { return ( diff --git a/src/pages/Campaign/ModalRegisterCampaignCaptcha.tsx b/src/pages/Campaign/ModalRegisterCampaignCaptcha.tsx index 49a28fef71..5c1cb04d74 100644 --- a/src/pages/Campaign/ModalRegisterCampaignCaptcha.tsx +++ b/src/pages/Campaign/ModalRegisterCampaignCaptcha.tsx @@ -1,13 +1,12 @@ import { Trans } from '@lingui/macro' -import axios from 'axios' import { createRef, memo, useCallback } from 'react' import ReCAPTCHA from 'react-google-recaptcha' import { Text } from 'rebass' +import { useJoinCampaignMutation } from 'services/campaign' import styled from 'styled-components' import { ModalCenter } from 'components/Modal' import { GOOGLE_RECAPTCHA_KEY } from 'constants/env' -import { CAMPAIGN_BASE_URL } from 'constants/index' import { useActiveWeb3React } from 'hooks' import useTheme from 'hooks/useTheme' import { ApplicationModal } from 'state/application/actions' @@ -44,7 +43,7 @@ const ModalRegisterCampaignCaptcha = ({ refreshListCampaign }: { refreshListCamp const theme = useTheme() const { account } = useActiveWeb3React() - + const [joinCampaign] = useJoinCampaignMutation() // Create an event handler, so you can call the verification on button click event or form submit const handleReCaptchaVerify = useCallback(async () => { if (!recaptchaCampaign.id || !account) return @@ -64,18 +63,13 @@ const ModalRegisterCampaignCaptcha = ({ refreshListCampaign }: { refreshListCamp const token = await recaptchaRef.current.getValue() await new Promise(r => setTimeout(r, 750)) toggleRegisterCampaignCaptchaModal() - const response = await axios({ - method: 'POST', - url: `${CAMPAIGN_BASE_URL}/${recaptchaCampaign.id}/participants`, - data: { - token, - address: account, - }, - }) - if (response.status === 200) { - refreshListCampaign() - toggleRegisterCampaignSuccessModal() - } + await joinCampaign({ + token, + address: account, + recaptchaId: recaptchaCampaign.id, + }).unwrap() + refreshListCampaign() + toggleRegisterCampaignSuccessModal() } catch (err) { console.error(err) } finally { @@ -91,6 +85,7 @@ const ModalRegisterCampaignCaptcha = ({ refreshListCampaign }: { refreshListCamp updateRecaptchaCampaignId, updateRecaptchaCampaignLoading, refreshListCampaign, + joinCampaign, ]) return ( diff --git a/src/pages/Campaign/ModalSelectCampaign.tsx b/src/pages/Campaign/ModalSelectCampaign.tsx index 6d5d8cb6f0..52510faf5e 100644 --- a/src/pages/Campaign/ModalSelectCampaign.tsx +++ b/src/pages/Campaign/ModalSelectCampaign.tsx @@ -27,7 +27,7 @@ const ModalSelectCampaign = (props: { }, 200) } return ( - +
diff --git a/src/pages/Campaign/index.tsx b/src/pages/Campaign/index.tsx index 77057cdc1f..7f073f2521 100644 --- a/src/pages/Campaign/index.tsx +++ b/src/pages/Campaign/index.tsx @@ -1,113 +1,28 @@ -import { ZERO } from '@kyberswap/ks-sdk-classic' -import { Fraction } from '@kyberswap/ks-sdk-core' -import axios from 'axios' -import { parseUnits } from 'ethers/lib/utils' -import JSBI from 'jsbi' -import { stringify } from 'querystring' import { useCallback, useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useLocation, useNavigate } from 'react-router-dom' -import useSWR, { mutate } from 'swr' -import useSWRImmutable from 'swr/immutable' +import { useGetCampaignsQuery, useGetLeaderboardQuery } from 'services/campaign' -import { APP_PATHS, CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, RESERVE_USD_DECIMALS, SWR_KEYS } from 'constants/index' +import { APP_PATHS, CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, EMPTY_ARRAY } from 'constants/index' import { useActiveWeb3React } from 'hooks' import useParsedQueryString from 'hooks/useParsedQueryString' import { - CampaignData, CampaignLeaderboard, - CampaignLeaderboardRanking, - CampaignLeaderboardReward, - CampaignLuckyWinner, - CampaignState, - CampaignStatus, - RewardDistribution, setCampaignDataByPage, setLastTimeRefreshData, setLoadingCampaignData, setLoadingCampaignDataError, setLoadingSelectedCampaignLeaderboard, - setLoadingSelectedCampaignLuckyWinners, setSelectedCampaign, setSelectedCampaignLeaderboard, - setSelectedCampaignLuckyWinners, } from 'state/campaigns/actions' import { AppState } from 'state/index' -import { SerializedToken } from 'state/user/actions' import { getCampaignIdFromSlug, getSlugUrlCampaign } from 'utils/campaign' -import CampaignContent from './CampaignContent' +import CampaignContent, { MINUTE_TO_REFRESH } from './CampaignContent' const MAXIMUM_ITEMS_PER_REQUEST = 10 -const getCampaignStatus = ({ endTime, startTime }: CampaignData) => { - const now = Date.now() - return endTime <= now ? CampaignStatus.ENDED : startTime >= now ? CampaignStatus.UPCOMING : CampaignStatus.ONGOING -} - -const formatRewards = (rewards: CampaignLeaderboardReward[]) => - rewards?.map( - (item: any): CampaignLeaderboardReward => ({ - rewardAmount: new Fraction( - item.RewardAmount, - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(item?.Token?.decimals ?? 18)), - ), - ref: item.Ref, - claimed: item.Claimed, - token: item.Token, - }), - ) || [] - -const formatLeaderboardData = (data: CampaignLeaderboard) => { - const leaderboard: CampaignLeaderboard = { - ...data, - rankings: data.rankings - ? data.rankings.map( - (item: any): CampaignLeaderboardRanking => ({ - userAddress: item.userAddress, - totalPoint: item.totalPoint, - rankNo: item.rankNo, - rewardAmount: new Fraction( - item.rewardAmount || ZERO, - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(item?.token?.decimals ?? 18)), - ), - rewardAmountUsd: new Fraction( - parseUnits(item?.rewardAmountUSD?.toString() || '0', RESERVE_USD_DECIMALS).toString(), - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(RESERVE_USD_DECIMALS)), - ), - rewardInUSD: item.rewardInUSD, - token: item.token, - }), - ) - : [], - rewards: formatRewards(data.rewards), - } - return leaderboard -} - -const fetchLeaderBoard = ({ - pageNumber, - userAddress, - lookupAddress, - campaignId, -}: { - pageNumber: number - userAddress: string - lookupAddress: string - campaignId: number -}) => { - return axios({ - method: 'GET', - url: SWR_KEYS.getLeaderboard(campaignId), - params: { - pageSize: CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, - pageNumber, - userAddress, - lookupAddress, - }, - }).then(({ data }) => formatLeaderboardData(data.data)) -} - const LEADERBOARD_DEFAULT: CampaignLeaderboard = { finalizedAt: 0, distributedRewardsAt: 0, @@ -117,115 +32,6 @@ const LEADERBOARD_DEFAULT: CampaignLeaderboard = { rewards: [], } -const formatListCampaign = (response: CampaignData[]) => { - const campaigns: CampaignData[] = response.map((item: CampaignData) => ({ - ...item, - startTime: item.startTime * 1000, - endTime: item.endTime * 1000, - })) - const formattedCampaigns: CampaignData[] = campaigns.map((campaign: any) => { - const rewardDistribution: RewardDistribution[] = [] - if (campaign.rewardDistribution.single) { - campaign.rewardDistribution.single.forEach( - ({ - amount, - rank, - token, - rewardInUSD, - }: { - amount: string - rank: number - token: SerializedToken - rewardInUSD: boolean - }) => { - rewardDistribution.push({ - type: 'Single', - amount, - rank, - token, - rewardInUSD, - }) - }, - ) - } - if (campaign.rewardDistribution.range) { - campaign.rewardDistribution.range.forEach( - ({ - from, - to, - amount, - token, - rewardInUSD, - }: { - from: number - to: number - amount: string - token: SerializedToken - rewardInUSD: boolean - }) => { - rewardDistribution.push({ - type: 'Range', - from, - to, - amount, - token, - rewardInUSD, - }) - }, - ) - } - if (campaign.rewardDistribution.random) { - campaign.rewardDistribution.random.forEach( - ({ - from, - to, - amount, - numberOfWinners, - token, - rewardInUSD, - }: { - from: number - to: number - amount: string - numberOfWinners: number - token: SerializedToken - rewardInUSD: boolean - }) => { - rewardDistribution.push({ - type: 'Random', - from, - to, - amount, - nWinners: numberOfWinners, - token, - rewardInUSD, - }) - }, - ) - } - if (campaign?.userInfo?.tradingVolume) campaign.userInfo.tradingVolume = Number(campaign.userInfo.tradingVolume) - if (campaign.userInfo) campaign.userInfo.rewards = formatRewards(campaign.userInfo.rewards) - return { - ...campaign, - rewardDistribution, - status: getCampaignStatus(campaign), - eligibleTokens: campaign.eligibleTokens.map( - ({ chainId, name, symbol, address, logoURI, decimals }: SerializedToken) => { - return { - chainId, - name, - symbol, - address, - logoURI, - decimals, - } - }, - ), - } - }) - return formattedCampaigns -} - const getQueryDefault = (userAddress: string | undefined) => ({ campaignName: '', userAddress, @@ -247,7 +53,6 @@ export default function CampaignsUpdater() { }, []) const { data: currentCampaigns } = useSelector((state: AppState) => state.campaigns) - const getCampaignUrl = useCallback(() => `${SWR_KEYS.getListCampaign}?${stringify(queryParams)}`, [queryParams]) const loadMoreCampaign = useCallback(() => { if (!currentCampaigns.length) return @@ -259,24 +64,22 @@ export default function CampaignsUpdater() { }, [account, setQueryParams]) const { - data: campaignData, - isValidating: isLoadingCampaignData, + data: campaignData = EMPTY_ARRAY, + isFetching: isLoadingCampaignData, error: loadingCampaignDataError, - } = useSWR(getCampaignUrl(), async url => { - try { - const { data: response } = await axios.get(url) - const campaigns: CampaignData[] = response.data - setHasMoreCampaign(campaigns.length === MAXIMUM_ITEMS_PER_REQUEST) - return formatListCampaign(campaigns) - } catch (error) { - return [] - } - }) + refetch, + } = useGetCampaignsQuery(queryParams) + + useEffect(() => { + setHasMoreCampaign(campaignData.length === MAXIMUM_ITEMS_PER_REQUEST) + }, [campaignData]) const refreshListCampaign = useCallback(async () => { - await mutate(getCampaignUrl()) - dispatch(setLastTimeRefreshData()) - }, [getCampaignUrl, dispatch]) + try { + await refetch().unwrap() + dispatch(setLastTimeRefreshData()) + } catch (error) {} + }, [refetch, dispatch]) const slug = pathname.replace(APP_PATHS.CAMPAIGN, '') const { selectedCampaignId = getCampaignIdFromSlug(slug) } = useParsedQueryString<{ selectedCampaignId: string }>() @@ -294,7 +97,7 @@ export default function CampaignsUpdater() { replace: true, }) } - if (selectedCampaignId === undefined) { + if (!selectedCampaignId) { navigateFirsOne() return } @@ -311,7 +114,7 @@ export default function CampaignsUpdater() { }, [dispatch, isLoadingCampaignData]) useEffect(() => { - dispatch(setLoadingCampaignDataError(loadingCampaignDataError)) + dispatch(setLoadingCampaignDataError(!!loadingCampaignDataError)) }, [dispatch, loadingCampaignDataError]) /**********************CAMPAIGN LEADERBOARD**********************/ @@ -319,33 +122,15 @@ export default function CampaignsUpdater() { const { selectedCampaignLeaderboardPageNumber, selectedCampaignLeaderboardLookupAddress, selectedCampaign } = useSelector((state: AppState) => state.campaigns) - const { data: leaderboard, isValidating: isLoadingLeaderboard } = useSWRImmutable( - selectedCampaign - ? [ - selectedCampaign, - SWR_KEYS.getLeaderboard(selectedCampaign.id), - selectedCampaignLeaderboardPageNumber, - selectedCampaignLeaderboardLookupAddress, - account, - ] - : null, - async () => { - if (!selectedCampaign) { - return LEADERBOARD_DEFAULT - } - - try { - return fetchLeaderBoard({ - campaignId: selectedCampaign.id, - pageNumber: selectedCampaignLeaderboardPageNumber, - userAddress: account ?? '', - lookupAddress: selectedCampaignLeaderboardLookupAddress, - }) - } catch (err) { - console.error(err) - return LEADERBOARD_DEFAULT - } + const { currentData: leaderboard = LEADERBOARD_DEFAULT, isFetching: isLoadingLeaderboard } = useGetLeaderboardQuery( + { + campaignId: selectedCampaign?.id || 0, + pageNumber: selectedCampaignLeaderboardPageNumber, + userAddress: account ?? '', + lookupAddress: selectedCampaignLeaderboardLookupAddress, + pageSize: CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, }, + { skip: !selectedCampaign?.id, pollingInterval: MINUTE_TO_REFRESH * 1000 }, ) useEffect(() => { @@ -358,63 +143,6 @@ export default function CampaignsUpdater() { dispatch(setLoadingSelectedCampaignLeaderboard(isLoadingLeaderboard)) }, [dispatch, isLoadingLeaderboard]) - /**********************CAMPAIGN LUCKY WINNERS**********************/ - - const { selectedCampaignLuckyWinnersPageNumber, selectedCampaignLuckyWinnersLookupAddress } = useSelector( - (state: AppState) => state.campaigns, - ) - - const { data: luckyWinners, isValidating: isLoadingLuckyWinners } = useSWRImmutable( - selectedCampaign - ? [ - selectedCampaign, - SWR_KEYS.getLuckyWinners(selectedCampaign.id), - selectedCampaignLuckyWinnersPageNumber, - selectedCampaignLuckyWinnersLookupAddress, - ] - : null, - async () => { - if (!selectedCampaign || selectedCampaign.campaignState === CampaignState.CampaignStateReady) return [] - - try { - const response = await axios({ - method: 'GET', - url: SWR_KEYS.getLuckyWinners(selectedCampaign.id), - params: { - pageSize: CAMPAIGN_LEADERBOARD_ITEM_PER_PAGE, - pageNumber: selectedCampaignLuckyWinnersPageNumber, - lookupAddress: selectedCampaignLuckyWinnersLookupAddress, - }, - }) - const data = response.data.data - const luckyWinners: CampaignLuckyWinner[] = data.map( - (item: any): CampaignLuckyWinner => ({ - userAddress: item.userAddress, - rewardAmount: new Fraction( - item.rewardAmount, - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(item?.token?.decimals ?? 18)), - ), - token: item.token, - }), - ) - return luckyWinners - } catch (err) { - console.error(err) - return [] - } - }, - ) - - useEffect(() => { - if (luckyWinners !== undefined) { - dispatch(setSelectedCampaignLuckyWinners({ luckyWinners: luckyWinners })) - } - }, [dispatch, luckyWinners]) - - useEffect(() => { - dispatch(setLoadingSelectedCampaignLuckyWinners(isLoadingLuckyWinners)) - }, [dispatch, isLoadingLuckyWinners]) - return ( { + const now = Date.now() + return endTime <= now ? CampaignStatus.ENDED : startTime >= now ? CampaignStatus.UPCOMING : CampaignStatus.ONGOING +} + +const formatRewards = (rewards: CampaignLeaderboardReward[]) => + rewards?.map( + (item: any): CampaignLeaderboardReward => ({ + rewardAmount: new Fraction( + item.RewardAmount, + JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(item?.Token?.decimals ?? 18)), + ), + ref: item.Ref, + claimed: item.Claimed, + token: item.Token, + }), + ) || [] + +const formatListCampaign = (response: CampaignData[]) => { + const campaigns: CampaignData[] = response.map((item: CampaignData) => ({ + ...item, + startTime: item.startTime * 1000, + endTime: item.endTime * 1000, + })) + const formattedCampaigns: CampaignData[] = campaigns.map((campaign: any) => { + const rewardDistribution: RewardDistribution[] = [] + if (campaign.rewardDistribution.single) { + campaign.rewardDistribution.single.forEach( + ({ + amount, + rank, + token, + rewardInUSD, + }: { + amount: string + rank: number + token: SerializedToken + rewardInUSD: boolean + }) => { + rewardDistribution.push({ + type: 'Single', + amount, + rank, + token, + rewardInUSD, + }) + }, + ) + } + if (campaign.rewardDistribution.range) { + campaign.rewardDistribution.range.forEach( + ({ + from, + to, + amount, + token, + rewardInUSD, + }: { + from: number + to: number + amount: string + token: SerializedToken + rewardInUSD: boolean + }) => { + rewardDistribution.push({ + type: 'Range', + from, + to, + amount, + token, + rewardInUSD, + }) + }, + ) + } + if (campaign.rewardDistribution.random) { + campaign.rewardDistribution.random.forEach( + ({ + from, + to, + amount, + numberOfWinners, + token, + rewardInUSD, + }: { + from: number + to: number + amount: string + numberOfWinners: number + token: SerializedToken + rewardInUSD: boolean + }) => { + rewardDistribution.push({ + type: 'Random', + from, + to, + amount, + nWinners: numberOfWinners, + token, + rewardInUSD, + }) + }, + ) + } + if (campaign?.userInfo?.tradingVolume) campaign.userInfo.tradingVolume = Number(campaign.userInfo.tradingVolume) + if (campaign.userInfo) campaign.userInfo.rewards = formatRewards(campaign.userInfo.rewards) + return { + ...campaign, + rewardDistribution, + status: getCampaignStatus(campaign), + eligibleTokens: campaign.eligibleTokens.map( + ({ chainId, name, symbol, address, logoURI, decimals }: SerializedToken) => { + return { + chainId, + name, + symbol, + address, + logoURI, + decimals, + } + }, + ), + } + }) + return formattedCampaigns +} + +const formatLeaderboardData = (data: CampaignLeaderboard) => { + const leaderboard: CampaignLeaderboard = { + ...data, + rankings: data.rankings + ? data.rankings.map( + (item: any): CampaignLeaderboardRanking => ({ + userAddress: item.userAddress, + totalPoint: item.totalPoint, + rankNo: item.rankNo, + rewardAmount: new Fraction( + item.rewardAmount || ZERO, + JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(item?.token?.decimals ?? 18)), + ), + rewardAmountUsd: new Fraction( + parseUnits(item?.rewardAmountUSD?.toString() || '0', RESERVE_USD_DECIMALS).toString(), + JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(RESERVE_USD_DECIMALS)), + ), + rewardInUSD: item.rewardInUSD, + token: item.token, + }), + ) + : [], + rewards: formatRewards(data.rewards), + } + return leaderboard +} + +const formatLuckyWinners = (data: any[]) => { + const luckyWinners: CampaignLuckyWinner[] = data.map( + (item: any): CampaignLuckyWinner => ({ + userAddress: item.userAddress, + rewardAmount: new Fraction( + item.rewardAmount, + JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(item?.token?.decimals ?? 18)), + ), + token: item.token, + }), + ) + return luckyWinners +} + +const formatTxs = (data: any[]) => { + return data.map( + (item: any): CampaignProofData => ({ + id: item.id, + chainId: parseInt(item.chainId), + utcTimestamp: new Date(item.time).getTime(), + txPoint: item.txPoint, + txHash: item.tx, + }), + ) +} + +const campaignApi = createApi({ + reducerPath: 'campaignApi', + baseQuery: fetchBaseQuery({ baseUrl: `${CAMPAIGN_BASE_URL}/api/v1/campaigns` }), + endpoints: builder => ({ + getCampaigns: builder.query({ + query: params => ({ + params, + url: '', + }), + transformResponse: (data: any) => formatListCampaign(data?.data || []), + }), + getLeaderboard: builder.query< + any, + { pageSize: number; pageNumber: number; userAddress: string; lookupAddress: string; campaignId: number } + >({ + query: ({ campaignId, ...params }) => ({ + params, + url: `/${campaignId}/leaderboard`, + }), + transformResponse: (data: any) => formatLeaderboardData(data?.data), + }), + getLuckyWinners: builder.query< + CampaignLuckyWinner[], + { pageSize: number; pageNumber: number; lookupAddress: string; campaignId: number } + >({ + query: ({ campaignId, ...params }) => ({ + params, + url: `/${campaignId}/lucky-winners`, + }), + transformResponse: (data: any) => formatLuckyWinners(data?.data || []), + }), + getTxsCampaign: builder.query< + CampaignProofData[], + { limit: number; offset: number; userAddress: string; campaignId: number } + >({ + query: ({ campaignId, ...params }) => ({ + params, + url: `/${campaignId}/proofs`, + }), + transformResponse: (data: any) => formatTxs(data?.data || []), + }), + joinCampaign: builder.mutation({ + query: ({ recaptchaId, ...body }) => ({ + body, + method: 'POST', + url: `/${recaptchaId}/participants`, + }), + }), + }), +}) + +export const { + useGetCampaignsQuery, + useGetLeaderboardQuery, + useGetLuckyWinnersQuery, + useJoinCampaignMutation, + useGetTxsCampaignQuery, +} = campaignApi + +export default campaignApi diff --git a/src/state/campaigns/actions.ts b/src/state/campaigns/actions.ts index d8c3013db9..79e77b3f85 100644 --- a/src/state/campaigns/actions.ts +++ b/src/state/campaigns/actions.ts @@ -145,7 +145,7 @@ export const setLoadingCampaignData = createAction('campaigns/setLoadin export const setLastTimeRefreshData = createAction('campaigns/setLastTimeRefreshData') -export const setLoadingCampaignDataError = createAction('campaigns/setLoadingCampaignDataError') +export const setLoadingCampaignDataError = createAction('campaigns/setLoadingCampaignDataError') export const setSelectedCampaign = createAction<{ campaign: CampaignData }>('campaigns/setSelectedCampaign') @@ -164,18 +164,5 @@ export const setSelectedCampaignLeaderboardLookupAddress = createAction( export const setClaimingCampaignRewardId = createAction('campaigns/setClaimingCampaignRewardId') -export const setSelectedCampaignLuckyWinners = createAction<{ luckyWinners: CampaignLuckyWinner[] }>( - 'campaigns/setSelectedCampaignLuckyWinners', -) -export const setLoadingSelectedCampaignLuckyWinners = createAction( - 'campaigns/setLoadingSelectedCampaignLuckyWinners', -) -export const setSelectedCampaignLuckyWinnersPageNumber = createAction( - 'campaigns/setSelectedCampaignLuckyWinnersPageNumber', -) -export const setSelectedCampaignLuckyWinnersLookupAddress = createAction( - 'campaigns/setSelectedCampaignLuckyWinnersLookupAddress', -) - export const setRecaptchaCampaignId = createAction('campaigns/setRecaptchaCampaignId') export const setRecaptchaCampaignLoading = createAction('campaigns/setRecaptchaCampaignLoading') diff --git a/src/state/campaigns/hooks.ts b/src/state/campaigns/hooks.ts index b38df444a7..2151d9f883 100644 --- a/src/state/campaigns/hooks.ts +++ b/src/state/campaigns/hooks.ts @@ -10,8 +10,6 @@ import { setRecaptchaCampaignLoading, setSelectedCampaignLeaderboardLookupAddress, setSelectedCampaignLeaderboardPageNumber, - setSelectedCampaignLuckyWinnersLookupAddress, - setSelectedCampaignLuckyWinnersPageNumber, } from 'state/campaigns/actions' import { AppState } from 'state/index' @@ -31,20 +29,6 @@ export function useSelectedCampaignLeaderboardPageNumberManager(): [number, (pag return [selectedCampaignLeaderboardPageNumber, updateSelectedCampaignLeaderboardPageNumberCallback] } -export function useSelectedCampaignLuckyWinnerPageNumber(): [number, (page: number) => void] { - const page = useSelector((state: AppState) => state.campaigns.selectedCampaignLuckyWinnersPageNumber) - const dispatch = useDispatch() - - const setPage = useCallback( - (newPageNumber: number) => { - dispatch(setSelectedCampaignLuckyWinnersPageNumber(newPageNumber)) - }, - [dispatch], - ) - - return [page, setPage] -} - export function useSelectedCampaignLeaderboardLookupAddressManager() { const selectedCampaignLeaderboardLookupAddress = useSelector( (state: AppState) => state.campaigns.selectedCampaignLeaderboardLookupAddress, @@ -64,25 +48,6 @@ export function useSelectedCampaignLeaderboardLookupAddressManager() { ) } -export function useSelectedCampaignLuckyWinnersLookupAddressManager() { - const selectedCampaignLuckyWinnersLookupAddress = useSelector( - (state: AppState) => state.campaigns.selectedCampaignLuckyWinnersLookupAddress, - ) - const dispatch = useDispatch() - - const updateSelectedCampaignLuckyWinnersLookupAddressCallback = useCallback( - (newLookupAddress: string) => { - dispatch(setSelectedCampaignLuckyWinnersLookupAddress(newLookupAddress)) - }, - [dispatch], - ) - - return useMemo( - () => [selectedCampaignLuckyWinnersLookupAddress, updateSelectedCampaignLuckyWinnersLookupAddressCallback] as const, - [selectedCampaignLuckyWinnersLookupAddress, updateSelectedCampaignLuckyWinnersLookupAddressCallback], - ) -} - export function useRecaptchaCampaignManager() { const recaptchaCampaign = useSelector((state: AppState) => state.campaigns.recaptchaCampaign) const dispatch = useDispatch() diff --git a/src/state/campaigns/reducer.ts b/src/state/campaigns/reducer.ts index 35c921bb91..e9d12693dc 100644 --- a/src/state/campaigns/reducer.ts +++ b/src/state/campaigns/reducer.ts @@ -3,7 +3,6 @@ import { createReducer } from '@reduxjs/toolkit' import { CampaignData, CampaignLeaderboard, - CampaignLuckyWinner, setCampaignData, setCampaignDataByPage, setClaimingCampaignRewardId, @@ -11,35 +10,25 @@ import { setLoadingCampaignData, setLoadingCampaignDataError, setLoadingSelectedCampaignLeaderboard, - setLoadingSelectedCampaignLuckyWinners, setRecaptchaCampaignId, setRecaptchaCampaignLoading, setSelectedCampaign, setSelectedCampaignLeaderboard, setSelectedCampaignLeaderboardLookupAddress, setSelectedCampaignLeaderboardPageNumber, - setSelectedCampaignLuckyWinners, - setSelectedCampaignLuckyWinnersLookupAddress, - setSelectedCampaignLuckyWinnersPageNumber, } from './actions' interface CampaignsState { readonly data: CampaignData[] readonly loadingCampaignData: boolean - readonly loadingCampaignDataError: Error | undefined + readonly loadingCampaignDataError: boolean readonly selectedCampaign: CampaignData | undefined readonly selectedCampaignLeaderboard: CampaignLeaderboard | undefined - readonly loadingCampaignLeaderboard: boolean readonly selectedCampaignLeaderboardPageNumber: number readonly selectedCampaignLeaderboardLookupAddress: string - readonly selectedCampaignLuckyWinners: CampaignLuckyWinner[] - readonly loadingCampaignLuckyWinners: boolean - readonly selectedCampaignLuckyWinnersPageNumber: number - readonly selectedCampaignLuckyWinnersLookupAddress: string - readonly claimingCampaignRewardId: number | null // id that is being claimed readonly recaptchaCampaign: { @@ -53,20 +42,14 @@ interface CampaignsState { const initialState: CampaignsState = { data: [], loadingCampaignData: true, - loadingCampaignDataError: undefined, + loadingCampaignDataError: false, selectedCampaign: undefined, selectedCampaignLeaderboard: undefined, - loadingCampaignLeaderboard: false, selectedCampaignLeaderboardPageNumber: 0, selectedCampaignLeaderboardLookupAddress: '', - selectedCampaignLuckyWinners: [], - loadingCampaignLuckyWinners: false, - selectedCampaignLuckyWinnersPageNumber: 0, - selectedCampaignLuckyWinnersLookupAddress: '', - claimingCampaignRewardId: null, recaptchaCampaign: { @@ -136,30 +119,7 @@ export default createReducer(initialState, builder => selectedCampaignLeaderboardLookupAddress: lookupAddress, } }) - .addCase(setSelectedCampaignLuckyWinners, (state, { payload: { luckyWinners } }) => { - return { - ...state, - selectedCampaignLuckyWinners: luckyWinners, - } - }) - .addCase(setLoadingSelectedCampaignLuckyWinners, (state, { payload: loading }) => { - return { - ...state, - loadingCampaignLuckyWinners: loading, - } - }) - .addCase(setSelectedCampaignLuckyWinnersPageNumber, (state, { payload: pageNumber }) => { - return { - ...state, - selectedCampaignLuckyWinnersPageNumber: pageNumber, - } - }) - .addCase(setSelectedCampaignLuckyWinnersLookupAddress, (state, { payload: lookupAddress }) => { - return { - ...state, - selectedCampaignLuckyWinnersLookupAddress: lookupAddress, - } - }) + .addCase(setRecaptchaCampaignId, (state, { payload: id }) => { return { ...state, diff --git a/src/state/index.ts b/src/state/index.ts index caffdcdc0c..337177cb2d 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -1,26 +1,27 @@ import { configureStore } from '@reduxjs/toolkit' import { load, save } from 'redux-localstorage-simple' +import announcementApi, { publicAnnouncementApi } from 'services/announcement' import blockServiceApi from 'services/blockService' +import campaignApi from 'services/campaign' import coingeckoApi from 'services/coingecko' +import crosschainApi from 'services/crossChain' +import earningApi from 'services/earning' +import geckoTerminalApi from 'services/geckoTermial' +import identifyApi from 'services/identity' +import knProtocolApi from 'services/knprotocol' +import ksSettingApi from 'services/ksSetting' import kyberAISubscriptionApi from 'services/kyberAISubscription' +import kyberDAO from 'services/kyberDAO' +import limitOrderApi from 'services/limitOrder' import priceAlertApi from 'services/priceAlert' import routeApi from 'services/route' +import socialApi from 'services/social' import tokenApi from 'services/token' import { ENV_LEVEL } from 'constants/env' import { ENV_TYPE } from 'constants/type' import kyberAIApi from 'pages/TrueSightV2/hooks/useKyberAIData' -import announcementApi, { publicAnnouncementApi } from '../services/announcement' -import crosschainApi from '../services/crossChain' -import earningApi from '../services/earning' -import geckoTerminalApi from '../services/geckoTermial' -import identifyApi from '../services/identity' -import knProtocolApi from '../services/knprotocol' -import ksSettingApi from '../services/ksSetting' -import kyberDAO from '../services/kyberDAO' -import limitOrderApi from '../services/limitOrder' -import socialApi from '../services/social' import application from './application/reducer' import authen from './authen/reducer' import burnProAmm from './burn/proamm/reducer' @@ -97,6 +98,7 @@ const store = configureStore({ [coingeckoApi.reducerPath]: coingeckoApi.reducer, [limitOrderApi.reducerPath]: limitOrderApi.reducer, + [campaignApi.reducerPath]: campaignApi.reducer, [kyberAIApi.reducerPath]: kyberAIApi.reducer, [kyberAISubscriptionApi.reducerPath]: kyberAISubscriptionApi.reducer, [kyberDAO.reducerPath]: kyberDAO.reducer, @@ -128,6 +130,7 @@ const store = configureStore({ .concat(coingeckoApi.middleware) .concat(limitOrderApi.middleware) .concat(kyberAIApi.middleware) + .concat(campaignApi.middleware) .concat(kyberAISubscriptionApi.middleware) .concat(announcementApi.middleware) .concat(publicAnnouncementApi.middleware)