From 8402e3f0430c9ec84b95ae1ae5fe8eaf1c071f2d Mon Sep 17 00:00:00 2001 From: wraeth-eth <104132113+wraeth-eth@users.noreply.github.com> Date: Sun, 27 Oct 2024 15:58:05 +1100 Subject: [PATCH] Update to prod (#4489) --- .../Project/ProjectTabs/ProjectTab.tsx | 10 +- .../components/TimelineViewSelector.tsx | 61 +++++++- .../inputs/FormattedNumberInput.tsx | 2 +- src/locales/messages.pot | 3 + .../PayProjectModal/PayProjectModal.tsx | 83 ++++++++--- .../PayProjectModal/hooks/usePayAmounts.ts | 137 ++++++++++++++++++ .../usePayProjectModal/usePayProjectTx.ts | 36 +++-- .../hooks/useReservedTokensSubPanel.test.ts | 2 +- .../hooks/useReservedTokensSubPanel.ts | 4 +- .../modals/V2V3DownloadActivityModal.tsx | 26 ++-- .../useV4FormatConfigurationTokenSection.ts | 20 ++- .../hooks/useV4ReservedTokensSubPanel.ts | 4 +- 12 files changed, 318 insertions(+), 70 deletions(-) create mode 100644 src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayAmounts.ts diff --git a/src/components/Project/ProjectTabs/ProjectTab.tsx b/src/components/Project/ProjectTabs/ProjectTab.tsx index 25dbbbbbb2..1f4b84856e 100644 --- a/src/components/Project/ProjectTabs/ProjectTab.tsx +++ b/src/components/Project/ProjectTabs/ProjectTab.tsx @@ -1,5 +1,5 @@ import { Tab } from '@headlessui/react' -import { ReactNode, useRef } from 'react' +import React, { ReactNode, useRef } from 'react' import { twMerge } from 'tailwind-merge' interface ProjectTabProps { @@ -26,14 +26,6 @@ export const ProjectTab: React.FC = ({ onClick={onClick} > {({ selected }) => { - if (selected && tabRef.current) { - tabRef.current.scrollIntoView({ - behavior: 'smooth', - block: 'nearest', - inline: 'start', - }) - } - return (
> +}) { + return ( + <> + + + + ) +} + +const MobileTimelineViewSelector = ({ timelineView, setTimelineView, }: { timelineView: ProjectTimelineView setTimelineView: React.Dispatch> -}) { +}) => { + const opts = (): { label: string; value: ProjectTimelineView }[] => [ + { + label: t`Volume`, + value: 'volume', + }, + { + label: t`In Juicebox`, + value: 'balance', + }, + { + label: t`Trending`, + value: 'trendingScore', + }, + ] + + const handleChange = (value: ProjectTimelineView) => { + setTimelineView(value) + } + + return ( + o.value === timelineView)} + onChange={v => handleChange(v.value)} + /> + ) +} + +const DesktopTimelineViewSelector = ({ + timelineView, + setTimelineView, +}: { + timelineView: ProjectTimelineView + setTimelineView: React.Dispatch> +}) => { const tab = (view: ProjectTimelineView) => { const selected = view === timelineView @@ -28,7 +78,7 @@ export default function TimelineViewSelector({ return (
) } - return ( -
+
{tab('volume')} {tab('balance')} {tab('trendingScore')} diff --git a/src/components/inputs/FormattedNumberInput.tsx b/src/components/inputs/FormattedNumberInput.tsx index a7f822ae45..944cea899e 100644 --- a/src/components/inputs/FormattedNumberInput.tsx +++ b/src/components/inputs/FormattedNumberInput.tsx @@ -110,5 +110,5 @@ export default function FormattedNumberInput({ {accessory &&
{accessory}
}
- ); + ) } diff --git a/src/locales/messages.pot b/src/locales/messages.pot index a6d5c40ccf..77a55f2016 100644 --- a/src/locales/messages.pot +++ b/src/locales/messages.pot @@ -296,6 +296,9 @@ msgstr "" msgid "Project tokens" msgstr "" +msgid "NFT Credits" +msgstr "" + msgid "You must grant permission to migrate your V1 tokens." msgstr "" diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/PayProjectModal.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/PayProjectModal.tsx index 466aede953..24b0fbb66d 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/PayProjectModal.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/PayProjectModal.tsx @@ -3,11 +3,15 @@ import EtherscanLink from 'components/EtherscanLink' import ExternalLink from 'components/ExternalLink' import { JuiceModal } from 'components/modals/JuiceModal' import { Formik } from 'formik' -import Image from "next/legacy/image" +import { useWallet } from 'hooks/Wallet' +import Image from 'next/legacy/image' +import { useNftCredits } from 'packages/v2v3/hooks/JB721Delegate/useNftCredits' +import React, { ReactNode } from 'react' import { twMerge } from 'tailwind-merge' import { helpPagePath } from 'utils/helpPagePath' import { MessageSection } from './components/MessageSection' import { ReceiveSection } from './components/ReceiveSection' +import { usePayAmounts } from './hooks/usePayAmounts' import { PayProjectModalFormValues, usePayProjectModal, @@ -16,8 +20,6 @@ import { export const PayProjectModal: React.FC = () => { const { open, - primaryAmount, - secondaryAmount, validationSchema, isTransactionPending, isTransactionConfirmed, @@ -27,6 +29,7 @@ export const PayProjectModal: React.FC = () => { setOpen, onPaySubmit, } = usePayProjectModal() + const { formattedTotalAmount } = usePayAmounts() return ( @@ -50,7 +53,7 @@ export const PayProjectModal: React.FC = () => { position="top" okLoading={props.isSubmitting || isTransactionPending} okButtonForm="PayProjectModalForm" - okText={t`Pay ${primaryAmount}`} + okText={t`Pay ${formattedTotalAmount.primaryAmount}`} cancelText={ isTransactionPending || isTransactionConfirmed ? t`Close` @@ -97,19 +100,7 @@ export const PayProjectModal: React.FC = () => { ) : ( <>
-
- - Total amount - -
- {primaryAmount}{' '} - {secondaryAmount && ( - - ({secondaryAmount}) - - )} -
-
+ @@ -172,3 +163,61 @@ export const PayProjectModal: React.FC = () => { ) } + +const AmountSection = () => { + const { userAddress } = useWallet() + const { data: nftCredits } = useNftCredits(userAddress) + const { formattedAmount, formattedNftCredits, formattedTotalAmount } = + usePayAmounts() + + const RowData = ({ + label, + primaryAmount, + secondaryAmount, + }: { + label: ReactNode + primaryAmount: ReactNode + secondaryAmount: ReactNode + }) => ( +
+ {label} +
+ {primaryAmount}{' '} + {secondaryAmount && ( + + ({secondaryAmount}) + + )} +
+
+ ) + + if (!nftCredits?.gt(0) || !formattedNftCredits) + return ( + + ) + + return ( +
+ + + +
+ ) +} diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayAmounts.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayAmounts.ts new file mode 100644 index 0000000000..074455272f --- /dev/null +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayAmounts.ts @@ -0,0 +1,137 @@ +import { useCurrencyConverter } from 'hooks/useCurrencyConverter' +import { useWallet } from 'hooks/Wallet' +import { useNftCredits } from 'packages/v2v3/hooks/JB721Delegate/useNftCredits' +import { + V2V3_CURRENCY_ETH, + V2V3_CURRENCY_USD, +} from 'packages/v2v3/utils/currency' +import { formatCurrencyAmount } from 'packages/v2v3/utils/formatCurrencyAmount' +import React from 'react' +import { fromWad, parseWad } from 'utils/format/formatNumber' +import { useProjectSelector } from '../../../../redux/hooks' +import { usePayProjectModal } from './usePayProjectModal/usePayProjectModal' + +export const usePayAmounts = () => { + const converter = useCurrencyConverter() + const { payAmount } = useProjectSelector(state => state.projectCart) + const { primaryAmount, secondaryAmount } = usePayProjectModal() + const { userAddress } = useWallet() + const { data: nftCreditsData } = useNftCredits(userAddress) + + const payAmountRaw = React.useMemo(() => { + if (!payAmount) return + + switch (payAmount.currency) { + case V2V3_CURRENCY_ETH: + return { + eth: parseWad(payAmount.amount), + usd: converter.weiToUsd(parseWad(payAmount.amount))!, + } + case V2V3_CURRENCY_USD: + return { + eth: converter.usdToWei(payAmount.amount), + usd: parseWad(payAmount.amount), + } + } + }, [converter, payAmount]) + + const appliedNFTCreditsRaw = React.useMemo(() => { + if (!payAmountRaw || !nftCreditsData) return + + const nftCreditsApplied = payAmountRaw.eth.lt(nftCreditsData) + ? payAmountRaw.eth + : nftCreditsData + + const eth = nftCreditsApplied + const usd = parseWad(converter.weiToUsd(nftCreditsApplied))! + + return { + eth, + usd, + } + }, [converter, nftCreditsData, payAmountRaw]) + + const formattedNftCredits = React.useMemo(() => { + if (!appliedNFTCreditsRaw || !payAmount) return + + switch (payAmount.currency) { + case V2V3_CURRENCY_ETH: + return { + primaryAmount: formatCurrencyAmount({ + amount: fromWad(appliedNFTCreditsRaw.eth), + currency: V2V3_CURRENCY_ETH, + }), + secondaryAmount: formatCurrencyAmount({ + amount: fromWad(appliedNFTCreditsRaw.usd), + currency: V2V3_CURRENCY_USD, + }), + } + case V2V3_CURRENCY_USD: + return { + primaryAmount: formatCurrencyAmount({ + amount: fromWad(appliedNFTCreditsRaw.usd), + currency: V2V3_CURRENCY_USD, + }), + secondaryAmount: formatCurrencyAmount({ + amount: fromWad(appliedNFTCreditsRaw.eth), + currency: V2V3_CURRENCY_ETH, + }), + } + } + }, [appliedNFTCreditsRaw, payAmount]) + + const formattedTotalAmount = React.useMemo(() => { + if (!payAmountRaw || !payAmount) return + + if (!appliedNFTCreditsRaw) { + return { + primaryAmount: primaryAmount, + secondaryAmount: secondaryAmount, + } + } + + const totalEth = payAmountRaw.eth.sub(appliedNFTCreditsRaw.eth) + const totalUsd = converter.weiToUsd(parseWad(totalEth)) + + const formattedEth = formatCurrencyAmount({ + amount: fromWad(totalEth), + currency: V2V3_CURRENCY_ETH, + }) + const formattedUsd = formatCurrencyAmount({ + amount: fromWad(totalUsd), + currency: V2V3_CURRENCY_USD, + }) + + switch (payAmount?.currency) { + case V2V3_CURRENCY_ETH: + return { + primaryAmount: formattedEth, + secondaryAmount: formattedUsd, + } + case V2V3_CURRENCY_USD: + return { + primaryAmount: formattedUsd, + secondaryAmount: formattedEth, + } + } + }, [ + appliedNFTCreditsRaw, + converter, + payAmount, + payAmountRaw, + primaryAmount, + secondaryAmount, + ]) + + return { + formattedAmount: { primaryAmount, secondaryAmount }, + formattedNftCredits: { + primaryAmount: formattedNftCredits?.primaryAmount, + secondaryAmount: formattedNftCredits?.secondaryAmount, + }, + formattedTotalAmount: { + primaryAmount: formattedTotalAmount?.primaryAmount, + secondaryAmount: formattedTotalAmount?.secondaryAmount, + }, + } +} diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayProjectModal/usePayProjectTx.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayProjectModal/usePayProjectTx.ts index f40a154bb2..b5d64a2cd7 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayProjectModal/usePayProjectTx.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayProjectModal/hooks/usePayProjectModal/usePayProjectTx.ts @@ -5,6 +5,7 @@ import { useCurrencyConverter } from 'hooks/useCurrencyConverter' import { ProjectPayReceipt } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectPageQueries' import { useProjectSelector } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/redux/hooks' import { NftRewardsContext } from 'packages/v2v3/contexts/NftRewards/NftRewardsContext' +import { useNftCredits } from 'packages/v2v3/hooks/JB721Delegate/useNftCredits' import { usePayETHPaymentTerminalTx } from 'packages/v2v3/hooks/transactor/usePayETHPaymentTerminalTx' import { useProjectHasErc20 } from 'packages/v2v3/hooks/useProjectHasErc20' import { V2V3_CURRENCY_ETH } from 'packages/v2v3/utils/currency' @@ -36,6 +37,7 @@ export const usePayProjectTx = ({ const { payAmount, chosenNftRewards } = useProjectSelector( state => state.projectCart, ) + const { data: nftCredits } = useNftCredits(userAddress) const { nftRewards: { rewardTiers }, } = useContext(NftRewardsContext) @@ -64,12 +66,20 @@ export const usePayProjectTx = ({ const weiAmount = useMemo(() => { if (!payAmount) { return parseWad(0) - } else if (payAmount.currency === V2V3_CURRENCY_ETH) { - return parseWad(payAmount.amount) - } else { - return converter.usdToWei(payAmount.amount) } - }, [payAmount, converter]) + let weiAmount = + payAmount.currency === V2V3_CURRENCY_ETH + ? parseWad(payAmount.amount) + : converter.usdToWei(payAmount.amount) + if (nftCredits) { + if (nftCredits.gte(weiAmount)) { + weiAmount = parseWad(0) + } else { + weiAmount = weiAmount.sub(nftCredits) + } + } + return weiAmount + }, [payAmount, converter, nftCredits]) const prepareDelegateMetadata = usePrepareDelegatePayMetadata(weiAmount, { nftRewards: chosenNftRewards, @@ -136,17 +146,17 @@ export const usePayProjectTx = ({ } }, [ - projectHasErc20, - buildPayReceipt, chosenNftRewards, - onTransactionConfirmedCallback, - onTransactionErrorCallback, - onTransactionPendingCallback, - payProjectTx, - rewardTiers, - weiAmount, userAddress, + rewardTiers, + payProjectTx, prepareDelegateMetadata, + weiAmount, + projectHasErc20, + onTransactionConfirmedCallback, + buildPayReceipt, + onTransactionPendingCallback, + onTransactionErrorCallback, ], ) } diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.test.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.test.ts index eeebceb6e3..9f58b80e9b 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.test.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.test.ts @@ -57,7 +57,7 @@ describe('useReservedTokensSubPanel', () => { const { result } = renderHook(useReservedTokensSubPanel) expect(result.current.reservedList).toEqual([ { - projectId: 1, + projectId: 0, address: '0x0000000000000000000000000000000000000000', percent: '97%', }, diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.ts index 866e083c40..30cb1ceab4 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/hooks/useReservedTokensSubPanel.ts @@ -31,7 +31,7 @@ export const useReservedTokensSubPanel = () => { if (reservedTokensSplits?.length === 0) return [ { - projectId, + projectId: 0, address: projectOwnerAddress!, percent: `${formatSplitPercent(ONE_BILLION_BIG)}%`, }, @@ -72,7 +72,7 @@ export const useReservedTokensSubPanel = () => { // If it isn't, add a split at the beginning which brings the total percentage to 100%. else processedSplits.unshift({ - projectId, + projectId: 0, address: projectOwnerAddress!, percent: `${formatSplitPercent(remainingPercentage)}%`, }) diff --git a/src/packages/v2v3/components/V2V3Project/modals/V2V3DownloadActivityModal.tsx b/src/packages/v2v3/components/V2V3Project/modals/V2V3DownloadActivityModal.tsx index ad288450ca..d02385d9c0 100644 --- a/src/packages/v2v3/components/V2V3Project/modals/V2V3DownloadActivityModal.tsx +++ b/src/packages/v2v3/components/V2V3Project/modals/V2V3DownloadActivityModal.tsx @@ -37,18 +37,20 @@ export default function V2V3DownloadActivityModal(props: ModalProps) { - setBlockNumber(val ? parseInt(val) : undefined)} - accessory={ - setBlockNumber(latestBlockNumber)} - disabled={blockNumber === latestBlockNumber} - /> - } - className="mb-4" - /> +
+ setBlockNumber(val ? parseInt(val) : undefined)} + accessory={ + setBlockNumber(latestBlockNumber)} + disabled={blockNumber === latestBlockNumber} + /> + } + /> +