Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
21f7c69
chore(): implemented fee details on the donate component UI
Jul 24, 2025
5edf7cf
chore(): style property of warning box fixed
Jul 24, 2025
c6d2c83
chore(): added fee section on donor collective card alongside tooltips
Jul 24, 2025
3761f52
feat: integrate collective fees into DonorCollectiveCard component
Jul 28, 2025
44c0baf
fix: improve error handling and fee display in DonorCollectiveCard
Aug 20, 2025
9866d72
fix: enhance error handling and fallback logic in useCollectiveFees hook
Aug 20, 2025
8161c30
Revert "fix: enhance error handling and fallback logic in useCollecti…
Aug 20, 2025
284039e
fix: update supportingLabel style in WalletCards component
Aug 20, 2025
2beb4c5
fix: optimize fee calculation in formatFlowRateToDaily function
Aug 20, 2025
21020dd
fix: improve error handling in fee calculation functions
Aug 20, 2025
d3dac2b
feat: enhance DonateComponent to display dynamic fee information
Aug 20, 2025
1a6fc6e
refactor: remove debug logging from useCollectiveFees and GoodCollect…
Aug 26, 2025
6f788ca
feat: integrate realtime stats into DonorCollectiveCard component
Aug 29, 2025
7219dbb
feat: display accumulated fees in ViewCollective component
Sep 2, 2025
5392e9b
feat: refactor useCollectiveFees to simplify fee retrieval logic
Sep 2, 2025
35359bc
refactor: remove useRealtimeStats from DonorCollectiveCard component
Sep 2, 2025
180f640
refactor: update useCollectiveFees and useRealtimeStats to utilize pr…
Sep 4, 2025
7326e53
Apply suggestions from code review
L03TJ3 Sep 4, 2025
69f99f1
Update packages/app/src/components/DonateComponent.tsx
L03TJ3 Sep 4, 2025
743ab6d
Update packages/app/src/components/DonateComponent.tsx
L03TJ3 Sep 4, 2025
4d4a756
style: adjust zIndex and overflow properties in WalletCards component
Sep 4, 2025
089cab0
Update packages/app/src/components/DonateComponent.tsx
L03TJ3 Sep 4, 2025
6cbee29
Merge branch 'Show-network-and-admin-fees' of https://github.com/Emek…
Sep 4, 2025
b007d93
fix: correct import statement and add fee documentation link in defaults
Sep 4, 2025
c45fc2b
style: remove zIndex from WalletCards styles and update zIndex logic …
Sep 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 79 additions & 47 deletions packages/app/src/components/DonateComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import { waitForTransactionReceipt } from '@wagmi/core';
import Decimal from 'decimal.js';
import { isEmpty } from 'lodash';
import moment from 'moment';
import { Box, HStack, Link, Text, useBreakpointValue, VStack } from 'native-base';
import { useCallback, useMemo, useState } from 'react';
import { Image, View } from 'react-native';
import { Box, HStack, Link, Text, useBreakpointValue, VStack } from 'native-base';
import { useAccount } from 'wagmi';
import { useParams } from 'react-router-native';
import Decimal from 'decimal.js';
import { waitForTransactionReceipt } from '@wagmi/core';
import { config } from './../config';
import { TransactionReceipt } from 'viem';
import { isEmpty } from 'lodash';
import moment from 'moment';
import { useAccount } from 'wagmi';
import { config } from './../config';

import RoundedButton from './RoundedButton';
import { useScreenSize } from '../theme/hooks';
import RoundedButton from './RoundedButton';

import BaseModal from './modals/BaseModal';
import { getDonateStyles } from '../utils';
import { InfoIconOrange } from '../assets';
import { useContractCalls, useGetTokenPrice } from '../hooks';
import { Collective } from '../models/models';
import { useApproveSwapTokenCallback } from '../hooks/useApproveSwapTokenCallback';
import { useGetTokenBalance } from '../hooks/useGetTokenBalance';
import { acceptablePriceImpact, Frequency, GDEnvTokens, SupportedNetwork } from '../models/constants';
import { InfoIconOrange } from '../assets';
import { SwapRouteState, useSwapRoute } from '../hooks/useSwapRoute';
import { useApproveSwapTokenCallback } from '../hooks/useApproveSwapTokenCallback';
import { acceptablePriceImpact, Frequency, GDEnvTokens, SupportedNetwork } from '../models/constants';
import { Collective } from '../models/models';
import { getDonateStyles } from '../utils';
import BaseModal from './modals/BaseModal';

import { ApproveTokenImg, PhoneImg, StreamWarning, ThankYouImg } from '../assets';
import { useToken, useTokenList } from '../hooks/useTokenList';
import { formatDecimalStringInput } from '../lib/formatDecimalStringInput';
import { formatNumberWithCommas } from '../lib/formatFiatCurrency';
import useCrossNavigate from '../routes/useCrossNavigate';
import FrequencySelector from './DonateFrequency';
import NumberInput from './NumberInput';
import { ApproveTokenImg, PhoneImg, StreamWarning, ThankYouImg } from '../assets';
import { formatNumberWithCommas } from '../lib/formatFiatCurrency';
type ConfigChainId = (typeof config.chains)[number]['id'];

interface DonateComponentProps {
Expand Down Expand Up @@ -113,7 +113,7 @@ const shouldWarning = (
};

const SwapValue = ({ swapValue }: { swapValue: number }) => (
<Text textAlign="right" fontSize="sm " color="goodGrey.25">
<Text textAlign="right" fontSize="sm " color="gray.400">
=
<Text variant="bold" fontWeight="700">
{' '}
Expand All @@ -127,7 +127,17 @@ const WarningBox = ({ content, explanationProps = {} }: any) => {
const Explanation = content.Explanation;

return (
<HStack space={2} backgroundColor="goodOrange.200" maxWidth="343" paddingY={3} paddingX={2}>
<HStack
space={2}
backgroundColor="goodOrange.200"
maxWidth="500"
borderRadius={15}
display={'flex'}
flexDirection="row"
alignItems="center"
justifyContent="center"
paddingY={3}
paddingX={2}>
<Image source={{ uri: InfoIconOrange }} style={{ width: 16, height: 16 }} />
<VStack space={4} maxWidth="100%">
<VStack space={1}>
Expand Down Expand Up @@ -176,7 +186,10 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
const [approveSwapModalVisible, setApproveSwapModalVisible] = useState(false);
const [thankYouModalVisible, setThankYouModalVisible] = useState(false);
const [startStreamingVisible, setStartStreamingVisible] = useState(false);
const [estimatedDuration, setEstimatedDuration] = useState<{ duration: number; endDate: string }>({
const [estimatedDuration, setEstimatedDuration] = useState<{
duration: number;
endDate: string;
}>({
duration: 0,
endDate: '',
});
Expand Down Expand Up @@ -219,7 +232,6 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
paddingRight: 2,
},
md: {
// maxWidth: 800,
width: '100%',
paddingLeft: 4,
paddingRight: 4,
Expand Down Expand Up @@ -268,7 +280,6 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
);

const approvalNotReady = handleApproveToken === undefined && currency.startsWith('G$') === false;
// const approvalNotReady = false;

const { supportFlowWithSwap, supportFlow, supportSingleTransferAndCall, supportSingleWithSwap } = useContractCalls(
collectiveId,
Expand All @@ -285,15 +296,12 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
);

const token = useToken(currency);
// const currencyDecimals = token.decimals;
const donorCurrencyBalance = useGetTokenBalance(token.address, address, chain?.id, true);

const totalDecimalDonation = new Decimal(decimalDonationAmount * (currency.includes('G$') ? 1 : Number(duration)));

const swapValue = currency.includes('G$') ? 0 : (totalDecimalDonation.toNumber() * altTokenPrice) / tokenPrice;

// const totalDonationFormatted = totalDecimalDonation.toDecimalPlaces(currencyDecimals, Decimal.ROUND_DOWN).toString();

const { isNonZeroDonation, isInsufficientBalance, isInsufficientLiquidity, isUnacceptablePriceImpact } =
shouldWarning(currency, donorCurrencyBalance, priceImpact, swapRouteStatus, totalDecimalDonation);

Expand Down Expand Up @@ -425,7 +433,10 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {

const estimatedEndDate = moment().add(estDuration, 'months').format('DD.MM.YY HH:mm');

setEstimatedDuration({ duration: estDuration, endDate: estimatedEndDate });
setEstimatedDuration({
duration: estDuration,
endDate: estimatedEndDate,
});
},
[currency, donorCurrencyBalance, frequency]
);
Expand Down Expand Up @@ -534,7 +545,7 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
<Text variant="bold" fontSize="lg">
Donation Frequency
</Text>
<Text>How do you want to donate</Text>
<Text>How do you want to donate ? </Text>
</VStack>
<FrequencySelector onSelect={onChangeFrequency} />

Expand All @@ -551,7 +562,7 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
<Box flexGrow={1} />
)}
</VStack>
{/* Amount and token */}

<VStack space={2} mb={8} zIndex={1}>
<VStack space={2} zIndex={1}>
<Text variant="bold" fontSize="lg">
Expand All @@ -562,16 +573,17 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
<NumberInput
type="token"
dropdownValue={currency}
inputValue={inputAmount?.toString()}
inputValue={inputAmount}
onSelect={onChangeCurrency}
onChangeAmount={onChangeAmount}
options={currencyOptions}
isWarning={isWarning}
withDuration={frequency !== 'One-Time'}
withDuration={frequency !== Frequency.OneTime}
/>
{frequency === 'One-Time' && !currency.startsWith('G$') && isNonZeroDonation && swapValue ? (
<SwapValue {...{ swapValue }} />
) : null}

{frequency === Frequency.OneTime && !currency.startsWith('G$') && isNonZeroDonation && swapValue > 0 && (
<SwapValue swapValue={swapValue} />
)}
</VStack>

<VStack space={2} maxWidth={650}>
Expand All @@ -586,10 +598,14 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
<NumberInput
type="number"
dropdownValue={currency}
inputValue={duration?.toString() ?? '1'}
inputValue={duration.toString()}
onSelect={onChangeCurrency}
onChangeAmount={onChangeRate}
/>{' '}
/>
{/* Swap value display under duration input */}
{!currency.startsWith('G$') && isNonZeroDonation && swapValue > 0 && (
<SwapValue swapValue={swapValue} />
)}
</>
) : null}
{currency.includes('G$') &&
Expand Down Expand Up @@ -617,19 +633,35 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
) : null}
</VStack>
</HStack>
{frequency !== 'One-Time' && currency === 'CELO' && isNonZeroDonation && swapValue ? (
<VStack space={2} alignItems="flex-start">
<Text variant="bold" fontSize="lg">
Total Donation Swap Amount:
</Text>
<VStack space="0">
<Text variant="bold" color="goodPurple.400" fontSize="2xl" textAlign="right">
CELO {decimalDonationAmount}

{/* Fee Information */}
<VStack space={2} backgroundColor="gray.50" p={2} borderRadius="lg">
<Text fontSize="sm" color="gray.700">
All donations incur a{' '}
<Text fontWeight="bold" color="black">
5%
</Text>{' '}
protocol fee, which contributes directly to{' '}
<Link
href="https://docs.gooddollar.org/wallet-and-products/goodcollective#what-are-the-fees-associated-with-starting-or-funding-a-goodcollective"
_text={{ color: 'blue.500', textDecoration: 'underline' }}>
GoodDollar UBI
</Link>
.{' '}
<Text>
All donations incur a{' '}
<Text fontWeight="bold" color="black">
3%
</Text>
<SwapValue {...{ swapValue }} />
</VStack>
</VStack>
) : null}
</Text>{' '}
<Link
href="https://docs.gooddollar.org/wallet-and-products/goodcollective#what-are-the-fees-associated-with-starting-or-funding-a-goodcollective"
_text={{ color: 'blue.500', textDecoration: 'underline' }}>
Manager Fee
</Link>
.
</Text>
</VStack>
</VStack>

<View style={{ gap: 16, flex: 1, zIndex: -1 }}>
Expand Down Expand Up @@ -670,7 +702,7 @@ const DonateComponent = ({ collective }: DonateComponentProps) => {
</Text>
)}
<Text>
Pressing Confirm will begin the donation {frequency !== Frequency.OneTime ? 'streaming ' : ''}process.
Pressing "Confirm" will begin the donation {frequency !== Frequency.OneTime ? 'streaming ' : ''}process.
You will need to confirm using your connected wallet. You may be asked to sign multiple transactions.
</Text>
</VStack>
Expand Down
Loading
Loading