diff --git a/lang/ca.json b/lang/ca.json index e871387c80..10586132ea 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -350,7 +350,7 @@ "label.givbacks_rewards": "Recompenses de GIVbacks", "label.givbacks_rewards_corresponding_to_the_current_round": "Les recompenses de GIVbacks corresponents a la ronda actual estan disponibles després que la ronda finalitzi. Si has donat a un projecte verificat i encara no tens recompenses per reclamar, és probable que els GIVbacks no s'hagin distribuït per a aquesta ronda, o que ja hagis reclamat les teves recompenses de GIVbacks.", "label.givbacks_rewards_donors_to_verified_projects": "GIVbacks recompensa els donants de projectes verificats amb GIV, potenciant Giveth com una força del bé impulsada pels donants.", - "label.givback_distributed_afer_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", + "label.givback_distributed_after_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", "label.give": "Dóna", "label.giveconomy": "GIVeconomy", "label.giveconomy_activities": "Activitats de GIVeconomy", @@ -1413,5 +1413,40 @@ "tooltip.donation.matching": "This estimation is based on the donations made so far this quadratic funding round and is not considering fraud analysis. Actual matching amount vary.", "ubi": "Renta bàsica", "water-and-sanitation": "Aigua i Sanejament", + "label.deposit": "dipòsit", + "label.how_do_you_want_to_donate": "Com vols donar?", + "label.this_feature_will_be_available_soon": "Aquesta funció estarà disponible aviat.", + "label.one_time_donation": "Donació Única", + "label.recurring_donation": "Donació Recurrent", + "label.make_a_recurring_donation_with": "Fes una donació recurrent amb ", + "label.provide_continuous_funding_by_streaming_your_donations_over_time": "Proporciona finançament continu fent les teves donacions periòdiques al llarg del temps.", + "label.creating_a_monthly_recurring_donation": "Creant una donació recurrent mensual", + "label.stream_balance": "Saldo de la Transmissió", + "label.select_token": "Selecciona Token", + "label.amount_to_donate_monthly": "Quantitat a donar mensualment", + "label.donate_to_this_project": "Dona a aquest projecte", + "label.per_month": "per Mes", + "label.stream_balance_runs_out_in": "El saldo del flux s'esgota en", + "label.months": "{count, plural, one { Mes} other { Mesos} }", + "label.month": "{count, plural, one { Mes} other { Mesos} }", + "label.top_up_stream_balance": "Recarrega el saldo del flux", + "label.you_are_supporting_other_projects_with_this_stream": "Estàs recolzant {count} altre projecte amb aquest flux", + "label.manage_recurring_donations": "Gestiona donacions recurrents", + "label.streams_powered_by": "Fluxos impulsats per", + "label.monthly": "Mensualment", + "label.donating_percentage_to": "Donant el {percentage} a ", + "label.modify_recurring_donation": "Modificar Donació Recurrent", + "label.stream_balances": "Saldos de la Transmissió", + "label.eligible_tokens": "Tokens Elegibles", + "label.you_have_stream_on_all_tokens": "Tens transmissió en tots els tokens", + "label.stream_runs_out_in": "El flux s'esgota en", + "label.funding_count_projects": "Finançant {count} {count, plural, one { Projecte} other { Projectes} }", + "label.recurring_donations_currently_only_available_on_optimism": "Les donacions recurrents actualment només estan disponibles en Optimism", + "label.switch_to_network_to_continue_donating": "Canvia a {network} per continuar donant a aquest projecte.", + "label.confirm_your_donation": "Confirma la teva donació", + "label.approving": "Aprovant", + "label.modify_stream_balance": "Modificar el Saldo de la Transmissió", + "label.funding": "Finançament", + "label.balance_runs_out_in": "El saldo s'esgota en", "label.unfortunately_passport_is_incompatible": "Desafortunadament, Passport no és compatible amb multisigs. Si us plau, intenta-ho amb la teva cartera personal" } diff --git a/lang/en.json b/lang/en.json index 139b66d373..71a3eb14f5 100644 --- a/lang/en.json +++ b/lang/en.json @@ -350,7 +350,7 @@ "label.givbacks_rewards": "GIVbacks Rewards", "label.givbacks_rewards_corresponding_to_the_current_round": "GIVbacks rewards corresponding to the current round become available after the round ends. If you donated to a verified project and do not yet have rewards to claim, it is likely that GIVbacks have not yet been distributed for that round, or that you claimed your GIVbacks rewards already.", "label.givbacks_rewards_donors_to_verified_projects": "GIVbacks rewards donors to verified projects with GIV, super-charging Giveth as a donor-driven force for good.", - "label.givback_distributed_afer_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", + "label.givback_distributed_after_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", "label.give": "Give", "label.giveconomy": "GIVeconomy", "label.giveconomy_activities": "GIVeconomy activities", @@ -1413,5 +1413,40 @@ "tooltip.donation.matching": "This estimation is based on the donations made so far this quadratic funding round and is not considering fraud analysis. Actual matching amount vary.", "ubi": "UBI", "water-and-sanitation": "Water & Sanitation", + "label.deposit": "deposit", + "label.how_do_you_want_to_donate": "How do you want to donate?", + "label.this_feature_will_be_available_soon": "This feature will be available soon.", + "label.one_time_donation": "One-Time Donation", + "label.recurring_donation": "Recurring Donation", + "label.make_a_recurring_donation_with": "Make a recurring donation with ", + "label.provide_continuous_funding_by_streaming_your_donations_over_time": "Provide continuous funding by streaming your donations over time.", + "label.creating_a_monthly_recurring_donation": "Creating a Monthly recurring donation", + "label.stream_balance": "Stream Balance", + "label.select_token": "Select Token", + "label.amount_to_donate_monthly": "Amount to donate Monthly", + "label.donate_to_this_project": "Donate to this project", + "label.per_month": "per Month", + "label.stream_balance_runs_out_in": "Stream balance runs out in", + "label.months": "{count, plural, one { Month} other { Months} }", + "label.month": "{count, plural, one { Month} other { Months} }", + "label.top_up_stream_balance": "Top-up stream balance", + "label.you_are_supporting_other_projects_with_this_stream": "You are supporting {count} other project with this stream", + "label.manage_recurring_donations": "Manage recurring donations", + "label.streams_powered_by": "Streams powered by", + "label.monthly": "Monthly", + "label.donating_percentage_to": "Donating {percentage} to ", + "label.modify_recurring_donation": "Modify Recurring Donation", + "label.stream_balances": "Stream Balances", + "label.eligible_tokens": "Tokens Elegibles", + "label.you_have_stream_on_all_tokens": "You have stream on all tokens", + "label.stream_runs_out_in": "Stream runs out in", + "label.funding_count_projects": "Funding {count} {count, plural, one { Project} other { Projects} }", + "label.recurring_donations_currently_only_available_on_optimism": "Recurring donations are currently only available on Optimism", + "label.switch_to_network_to_continue_donating": "Switch to {network} to continue donating to this project.", + "label.confirm_your_donation": "Confirm your donation", + "label.approving": "Approving", + "label.modify_stream_balance": "Modify Stream Balance", + "label.funding": "Funding", + "label.balance_runs_out_in": "Balance runs out in", "label.unfortunately_passport_is_incompatible": "Unfortunately Passport is not compatible with Multisigs, sign in with your regular wallet to verify your passport & make QF Donations." } diff --git a/lang/es.json b/lang/es.json index 04247fd08e..5f39172306 100644 --- a/lang/es.json +++ b/lang/es.json @@ -350,7 +350,7 @@ "label.givbacks_rewards": "Recompensas de GIVbacks", "label.givbacks_rewards_corresponding_to_the_current_round": "Recompensas de GIVbacks correspondientes a la ronda actual se harán disponibles después de que la ronda termine. Si donaste a un proyecto verificado y no tienes recompensas por reclamar, es muy probable que los GIVbacks no hayan sido distribuidos para esa ronda, o que ya hayas reclamado.", "label.givbacks_rewards_donors_to_verified_projects": "GIVbacks recompensa a los donantes de proyectos verificados con GIV, potenciando Giveth como una fuerza del bien impulsada por los donantes.", - "label.givback_distributed_afer_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", + "label.givback_distributed_after_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", "label.give": "Da", "label.giveconomy": "GIVeconomy", "label.giveconomy_activities": "Actividades en GIVeconomy", @@ -742,7 +742,6 @@ "label.show_your_support": "Muestra tu apoyo al futuro de las donaciones y desbloquea tu estilo único de Giveth minteando una de las primeras piezas NFT PFP inspiradas en Giveth.", "label.sign_in_to_giveth": "Inicia Sesión en Giveth", "label.sign_in_with_ethereum": "Ingresa con Ethereum", - "label.sign_in_with_ethereum": "Ingresa con Solana", "label.sign_message": "Firmar Mensaje", "label.sign_out": "Salir", "label.sign_wallet": "Firmar Billetera", @@ -1413,5 +1412,40 @@ "tooltip.donation.matching": "This estimation is based on the donations made so far this quadratic funding round and is not considering fraud analysis. Actual matching amount vary.", "ubi": "UBI", "water-and-sanitation": "Agua & Saneamiento", + "label.deposit": "depósito", + "label.how_do_you_want_to_donate": "¿Cómo quieres donar?", + "label.this_feature_will_be_available_soon": "Esta función estará disponible pronto.", + "label.one_time_donation": "Donación Única", + "label.recurring_donation": "Donación Recurrente", + "label.make_a_recurring_donation_with": "Haz una donación recurrente con ", + "label.provide_continuous_funding_by_streaming_your_donations_over_time": "Proporciona financiamiento continuo transmitiendo tus donaciones a lo largo del tiempo.", + "label.creating_a_monthly_recurring_donation": "Creando una donación recurrente mensual", + "label.stream_balance": "Saldo de Transmisión", + "label.select_token": "Seleccionar Token", + "label.amount_to_donate_monthly": "Cantidad a donar mensualmente", + "label.donate_to_this_project": "Donar a este proyecto", + "label.per_month": "por Mes", + "label.stream_balance_runs_out_in": "El saldo de la transmisión se agota en", + "label.months": "{count, plural, one { Mes} other { Meses} }", + "label.month": "{count, plural, one { Mes} other { Meses} }", + "label.top_up_stream_balance": "Recargar el saldo de la transmisión", + "label.you_are_supporting_other_projects_with_this_stream": "Estás apoyando {count} otro proyecto con esta transmisión", + "label.manage_recurring_donations": "Gestionar donaciones recurrentes", + "label.streams_powered_by": "Transmisiones potenciadas por", + "label.monthly": "Mensualmente", + "label.donating_percentage_to": "Donando el {percentage} a ", + "label.modify_recurring_donation": "Modificar Donación Recurrente", + "label.stream_balances": "Saldos de Transmisión", + "label.eligible_tokens": "Tokens Elegibles", + "label.you_have_stream_on_all_tokens": "Tienes transmisión en todos los tokens", + "label.stream_runs_out_in": "La transmisión se agota en", + "label.funding_count_projects": "Financiando {count} {count, plural, one { Proyecto} other { Proyectos} }", + "label.recurring_donations_currently_only_available_on_optimism": "Las donaciones recurrentes actualmente solo están disponibles en Optimism", + "label.switch_to_network_to_continue_donating": "Cambia a {network} para continuar donando a este proyecto.", + "label.confirm_your_donation": "Confirma tu donación", + "label.approving": "Aprobando", + "label.modify_stream_balance": "Modificar el Saldo de Transmisión", + "label.funding": "Financiamiento", + "label.balance_runs_out_in": "El saldo se agota en", "label.unfortunately_passport_is_incompatible": "Desafortunadamente, Passport no es compatible con multisigs. Por favor, inténtalo con tu billetera personal." } diff --git a/lang/nt_ca.json b/lang/nt_ca.json index d076fb6bbf..02c9f95f60 100644 --- a/lang/nt_ca.json +++ b/lang/nt_ca.json @@ -66,7 +66,7 @@ "label.get_your_giveth_name": "Get your Giveth Name.", "label.gitcoin_passport": "Gitcoin Passport", "label.givbacks": "GIVbacks", - "label.givback_distributed_afer_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", + "label.givback_distributed_after_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", "label.giveconomy": "GIVeconomy", "label.givers_nft": "Givers NFT", "label.givers_pfp_holders": "Givers PFP Holders will be able to claim their own unique ", diff --git a/lang/nt_es.json b/lang/nt_es.json index c35e4f8f8b..cfcf5fec89 100644 --- a/lang/nt_es.json +++ b/lang/nt_es.json @@ -57,7 +57,7 @@ "label.get_rewarded_with": "Get rewarded with up to ", "label.get_your_giveth_name": "Get your Giveth Name.", "label.gitcoin_passport": "Gitcoin Passport", - "label.givback_distributed_afer_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", + "label.givback_distributed_after_round": "GIV rewards from the GIVbacks program will be distributed after the end of the current round.", "label.giveconomy": "GIVeconomy", "label.givers_nft": "Givers NFT", "label.givers_pfp_holders": "Givers PFP Holders will be able to claim their own unique ", diff --git a/lang/t_ca.json b/lang/t_ca.json index b61d08e3ab..4d9601b671 100644 --- a/lang/t_ca.json +++ b/lang/t_ca.json @@ -53,7 +53,7 @@ "label.get_your_giveth_name": "Obtén el teu nom de Giveth.", "label.gitcoin_passport": "Passaport Gitcoin", "label.givbacks": "GIVbacks", - "label.givback_distributed_afer_round": "Les recompenses GIV del programa GIVbacks es distribuiran després de la fi de la ronda actual.", + "label.givback_distributed_after_round": "Les recompenses GIV del programa GIVbacks es distribuiran després de la fi de la ronda actual.", "label.giveconomy": "GIVeconomia", "label.givers_pfp_holders": "Els titulars de Givers PFP podran reclamar el seu propi únic ", "label.givpower": "GIVpower", diff --git a/lang/t_es.json b/lang/t_es.json index e44dbaf21b..4316556d61 100644 --- a/lang/t_es.json +++ b/lang/t_es.json @@ -44,7 +44,7 @@ "label.get_rewarded_with": "Obtén recompensas de hasta ", "label.get_your_giveth_name": "Obtén tu Nombre de Giveth.", "label.gitcoin_passport": "Pasaporte de Gitcoin", - "label.givback_distributed_afer_round": "Las recompensas GIV del programa GIVbacks se distribuirán después del final de la ronda actual.", + "label.givback_distributed_after_round": "Las recompensas GIV del programa GIVbacks se distribuirán después del final de la ronda actual.", "label.giveconomy": "Economía de Giveth", "label.givers_pfp_holders": "Los titulares de Givers PFP podrán reclamar su propio ", "label.givpower": "GIVpower", diff --git a/package.json b/package.json index 81382a3df5..0e6afdaf2e 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@apollo/client": "^3.8.3", - "@giveth/ui-design-system": "^1.11.17", + "@giveth/ui-design-system": "^1.11.19", "@reduxjs/toolkit": "^1.9.5", "@safe-global/api-kit": "^2.0.0", "@segment/snippet": "^4.15.3", @@ -28,6 +28,7 @@ "@solana/wallet-adapter-react-ui": "^0.9.34", "@solana/wallet-adapter-wallets": "^0.19.23", "@solana/web3.js": "^1.87.6", + "@superfluid-finance/sdk-core": "^0.6.12", "@uniswap/v3-sdk": "^3.10.0", "@web3auth/sign-in-with-solana": "^3.0.0", "@web3modal/wagmi": "^3.1.0", @@ -35,9 +36,9 @@ "base58": "^2.0.1", "bignumber.js": "^9.1.2", "deepmerge": "^4.2.2", - "ethers": "^6.7.1", + "ethers": "5.7.2", "framer-motion": "^10.12.12", - "graphql": "^16.8.0", + "graphql": "^16.8.1", "lodash.isequal": "^4.5.0", "lottie-react": "^2.3.1", "next": "^13.5.1", @@ -76,6 +77,7 @@ "@testing-library/react": "^13.3.0", "@types/apollo-upload-client": "^14.1.0", "@types/axios": "^0.14.0", + "@types/extract-files": "^8.1.3", "@types/jest": "^28.1.4", "@types/jest-axe": "^3.5.4", "@types/lodash.isequal": "^4.5.5", diff --git a/pages/test1.tsx b/pages/test1.tsx index e49d5067a2..de0625f212 100644 --- a/pages/test1.tsx +++ b/pages/test1.tsx @@ -75,7 +75,6 @@ const TestIndex = () => { query: FETCH_ALL_PROJECTS, fetchPolicy: 'network-only', }); - console.log('res', res); }; // console.log('gnosisValues', gnosisValues); diff --git a/pages/test2.tsx b/pages/test2.tsx index f073349bc6..231a5996cb 100644 --- a/pages/test2.tsx +++ b/pages/test2.tsx @@ -1,35 +1,13 @@ -import { useEffect } from 'react'; -import { erc20ABI, useAccount, useContractRead, useNetwork } from 'wagmi'; -import { getContract } from 'wagmi/actions'; -import { formatWeiHelper } from '@/helpers/number'; -import { approveERC20tokenTransfer } from '@/lib/stakingPool'; +import { useState } from 'react'; +import { useAccount, useNetwork } from 'wagmi'; +import { ModifySuperTokenModal } from '@/components/views/donate/ModifySuperToken/ModifySuperTokenModal'; import config from '@/configuration'; const YourApp = () => { + const [showModal, setShowModal] = useState(false); const { address, isConnected, status } = useAccount(); const { chain } = useNetwork(); const chainId = chain?.id; - // console.log('address', address, isConnected, status, chainId); - const { data } = useContractRead({ - address: '0xc916Ce4025Cb479d9BA9D798A80094a449667F5D', - abi: erc20ABI, - functionName: 'allowance', - }); - - useEffect(() => { - const contract = getContract({ - address: '0xc916Ce4025Cb479d9BA9D798A80094a449667F5D', - abi: erc20ABI, - }); - contract.read - .allowance([ - '0x8F48094a12c8F99d616AE8F3305D5eC73cBAA6b6', - '0x632AC305ed88817480d12155A7F1244cC182C298', - ]) - .then(res => console.log('Allow', formatWeiHelper(res.toString()))); - }, []); - - console.log('contractRead', getContract); return (
@@ -37,22 +15,19 @@ const YourApp = () => {
+ {showModal && ( + + )}
); }; diff --git a/public/images/logo/superfluid-logo.svg b/public/images/logo/superfluid-logo.svg new file mode 100644 index 0000000000..8ed1d8a51f --- /dev/null +++ b/public/images/logo/superfluid-logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/apollo/gql/gqlUser.ts b/src/apollo/gql/gqlUser.ts index ee5ce35e66..83972c06d2 100644 --- a/src/apollo/gql/gqlUser.ts +++ b/src/apollo/gql/gqlUser.ts @@ -147,6 +147,37 @@ export const VALIDATE_TOKEN = gql` } `; +export const FETCH_USER_STREAMS = ` + query FetchUserStreams($address: String!) { + streams( + where: { sender: $address } + ) { + id + sender { + id + } + receiver { + id + } + token { + __typename + id + name + symbol + decimals + isSuperToken + underlyingToken { + id + name + symbol + decimals + } + } + currentFlowRate + } + } +`; + export const FETCH_USER_GIVPOWER_BY_ADDRESS = ` query unipoolBalance($id: String!) { unipoolBalance( diff --git a/src/apollo/types/gqlTypes.ts b/src/apollo/types/gqlTypes.ts index 5ddbec356f..d6c3ceea1c 100644 --- a/src/apollo/types/gqlTypes.ts +++ b/src/apollo/types/gqlTypes.ts @@ -1,3 +1,4 @@ +import { type Address } from 'wagmi'; import { ICategory, IDonation, @@ -80,8 +81,8 @@ export interface IProjectAcceptedToken { id?: string; symbol: string; networkId: number; - address: `0x${string}`; - mainnetAddress?: `0x${string}`; + address: Address; + mainnetAddress?: Address; name: string; decimals: number; isGivbackEligible?: boolean; diff --git a/src/apollo/types/types.ts b/src/apollo/types/types.ts index abe926162f..2e83f1bdcc 100644 --- a/src/apollo/types/types.ts +++ b/src/apollo/types/types.ts @@ -29,7 +29,7 @@ export interface IEstimatedMatching { } export interface IProject { - id?: string; + id: string; title?: string; balance?: number; image?: string; diff --git a/src/artifacts/createProfile.json b/src/artifacts/createProfile.json new file mode 100644 index 0000000000..ffec9da489 --- /dev/null +++ b/src/artifacts/createProfile.json @@ -0,0 +1,838 @@ +{ + "abi": [ + { "inputs": [], "name": "ALLOCATION_ACTIVE", "type": "error" }, + { "inputs": [], "name": "ALLOCATION_NOT_ACTIVE", "type": "error" }, + { "inputs": [], "name": "ALLOCATION_NOT_ENDED", "type": "error" }, + { "inputs": [], "name": "ALREADY_INITIALIZED", "type": "error" }, + { "inputs": [], "name": "AMOUNT_MISMATCH", "type": "error" }, + { "inputs": [], "name": "ANCHOR_ERROR", "type": "error" }, + { "inputs": [], "name": "ARRAY_MISMATCH", "type": "error" }, + { "inputs": [], "name": "INVALID", "type": "error" }, + { "inputs": [], "name": "INVALID_ADDRESS", "type": "error" }, + { "inputs": [], "name": "INVALID_FEE", "type": "error" }, + { "inputs": [], "name": "INVALID_METADATA", "type": "error" }, + { "inputs": [], "name": "INVALID_REGISTRATION", "type": "error" }, + { "inputs": [], "name": "IS_APPROVED_STRATEGY", "type": "error" }, + { "inputs": [], "name": "MISMATCH", "type": "error" }, + { "inputs": [], "name": "NONCE_NOT_AVAILABLE", "type": "error" }, + { "inputs": [], "name": "NOT_APPROVED_STRATEGY", "type": "error" }, + { "inputs": [], "name": "NOT_ENOUGH_FUNDS", "type": "error" }, + { "inputs": [], "name": "NOT_INITIALIZED", "type": "error" }, + { "inputs": [], "name": "NOT_PENDING_OWNER", "type": "error" }, + { "inputs": [], "name": "POOL_ACTIVE", "type": "error" }, + { "inputs": [], "name": "POOL_INACTIVE", "type": "error" }, + { "inputs": [], "name": "RECIPIENT_ALREADY_ACCEPTED", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipientId", + "type": "address" + } + ], + "name": "RECIPIENT_ERROR", + "type": "error" + }, + { "inputs": [], "name": "RECIPIENT_NOT_ACCEPTED", "type": "error" }, + { "inputs": [], "name": "REGISTRATION_NOT_ACTIVE", "type": "error" }, + { "inputs": [], "name": "UNAUTHORIZED", "type": "error" }, + { "inputs": [], "name": "ZERO_ADDRESS", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "profileId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct Metadata", + "name": "metadata", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "anchor", + "type": "address" + } + ], + "name": "ProfileCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "profileId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct Metadata", + "name": "metadata", + "type": "tuple" + } + ], + "name": "ProfileMetadataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "profileId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "address", + "name": "anchor", + "type": "address" + } + ], + "name": "ProfileNameUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "profileId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ProfileOwnerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "profileId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "ProfilePendingOwnerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "ALLO_OWNER", + "outputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NATIVE", + "outputs": [ + { "internalType": "address", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + } + ], + "name": "acceptProfileOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "internalType": "address[]", + "name": "_members", + "type": "address[]" + } + ], + "name": "addMembers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" } + ], + "name": "anchorToProfileId", + "outputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { "internalType": "string", "name": "_name", "type": "string" }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "internalType": "struct Metadata", + "name": "_metadata", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_members", + "type": "address[]" + } + ], + "name": "createProfile", + "outputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_anchor", + "type": "address" + } + ], + "name": "getProfileByAnchor", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "internalType": "struct Metadata", + "name": "metadata", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "anchor", + "type": "address" + } + ], + "internalType": "struct IRegistry.Profile", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + } + ], + "name": "getProfileById", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "internalType": "struct Metadata", + "name": "metadata", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "anchor", + "type": "address" + } + ], + "internalType": "struct IRegistry.Profile", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleAdmin", + "outputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_member", + "type": "address" + } + ], + "name": "isMemberOfProfile", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "isOwnerOfProfile", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isOwnerOrMemberOfProfile", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "name": "profileIdToPendingOwner", + "outputs": [ + { "internalType": "address", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "", "type": "bytes32" } + ], + "name": "profilesById", + "outputs": [ + { "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { "internalType": "string", "name": "name", "type": "string" }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "internalType": "struct Metadata", + "name": "metadata", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "anchor", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_recipient", + "type": "address" + } + ], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "internalType": "address[]", + "name": "_members", + "type": "address[]" + } + ], + "name": "removeMembers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "protocol", + "type": "uint256" + }, + { + "internalType": "string", + "name": "pointer", + "type": "string" + } + ], + "internalType": "struct Metadata", + "name": "_metadata", + "type": "tuple" + } + ], + "name": "updateProfileMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { "internalType": "string", "name": "_name", "type": "string" } + ], + "name": "updateProfileName", + "outputs": [ + { + "internalType": "address", + "name": "anchor", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_profileId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_pendingOwner", + "type": "address" + } + ], + "name": "updateProfilePendingOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} diff --git a/src/components/AddTokenButton.tsx b/src/components/AddTokenButton.tsx index 71db3a8535..5658566952 100644 --- a/src/components/AddTokenButton.tsx +++ b/src/components/AddTokenButton.tsx @@ -2,9 +2,8 @@ import { brandColors, P } from '@giveth/ui-design-system'; import Image from 'next/image'; import { FC, useState, useEffect } from 'react'; import styled from 'styled-components'; -import { useAccount } from 'wagmi'; +import { type Address, useAccount } from 'wagmi'; import { Flex } from './styled-components/Flex'; -import { Address } from '@/types/config'; import { addToken } from '@/lib/metamask'; interface IAddGIVTokenButton { diff --git a/src/components/AmountInput.tsx b/src/components/AmountInput/AmountInput.tsx similarity index 51% rename from src/components/AmountInput.tsx rename to src/components/AmountInput/AmountInput.tsx index 648a99007d..fc5d0282df 100644 --- a/src/components/AmountInput.tsx +++ b/src/components/AmountInput/AmountInput.tsx @@ -5,22 +5,28 @@ import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; import BigNumber from 'bignumber.js'; import { formatWeiHelper } from '@/helpers/number'; -import { PoolStakingConfig, StakingPlatform } from '@/types/config'; -import { Flex } from './styled-components/Flex'; -import { NumericalInput } from '@/components/input/index'; +import { Flex } from '../styled-components/Flex'; +import { BaseInput } from '../input/BaseInput'; -interface IAmountInput { - maxAmount: bigint; +export interface IAmountInput { setAmount: Dispatch>; - poolStakingConfig: PoolStakingConfig; + decimals?: number; + className?: string; disabled?: boolean; + maxAmount?: bigint; + showMax?: boolean; + balanceUnit?: string; + showPercentage?: boolean; } export const AmountInput: FC = ({ maxAmount, setAmount, - poolStakingConfig, + className, + decimals = 18, disabled = false, + showMax = false, + showPercentage = false, }) => { const { formatMessage } = useIntl(); const [displayAmount, setDisplayAmount] = useState(''); @@ -28,6 +34,7 @@ export const AmountInput: FC = ({ const setAmountPercentage = useCallback( (percentage: number): void => { + if (!maxAmount) return; const newAmount = new BigNumber(maxAmount.toString()) .multipliedBy(percentage) .div(100) @@ -39,18 +46,18 @@ export const AmountInput: FC = ({ ? maxAmount : BigInt( new BigNumber(_displayAmount) - .multipliedBy(1e18) + .multipliedBy(10 ** decimals) .toFixed(0), ), ); }, - [maxAmount, setAmount], + [decimals, maxAmount, setAmount], ); const onUserInput = useCallback( (value: string) => { - const [, decimals] = value.split('.'); - if (decimals?.length > 6) { + const [, _decimals] = value.split('.'); + if (_decimals?.length > 6) { return; } setDisplayAmount(value); @@ -58,7 +65,7 @@ export const AmountInput: FC = ({ let valueBn = new BigNumber(0); try { - valueBn = new BigNumber(value).multipliedBy('1e18'); + valueBn = new BigNumber(value).multipliedBy(10 ** decimals); setAmount(BigInt(valueBn.toFixed(0))); } catch (error) { setAmount(0n); @@ -73,84 +80,85 @@ export const AmountInput: FC = ({ }); } }, - [setAmount], + [decimals, setAmount], ); return ( - <> - - - - {formatMessage({ id: 'label.available' })}:{' '} - - -   - {formatWeiHelper(maxAmount.toString())} -   - {poolStakingConfig.title} -   - {poolStakingConfig.platform !== - StakingPlatform.GIVETH && 'LP'} - - - { - if (disabled) return; - setAmountPercentage(100); - setActiveStep(100); - }} - > - Max - - - + {showMax && maxAmount !== undefined && ( + + + + {formatMessage({ id: 'label.available' })}:{' '} + + +   + {formatWeiHelper(maxAmount.toString())} +   + + + { + if (disabled) return; + setAmountPercentage(100); + setActiveStep(100); + }} + > + Max + + + )} + - - { - if (disabled) return; - setAmountPercentage(25); - setActiveStep(25); - }} - active={activeStep === 25} - > - 25% - - { - if (disabled) return; - setAmountPercentage(50); - setActiveStep(50); - }} - active={activeStep === 50} - > - 50% - - { - if (disabled) return; - setAmountPercentage(75); - setActiveStep(75); - }} - active={activeStep === 75} - > - 75% - - { - if (disabled) return; - setAmountPercentage(100); - setActiveStep(100); - }} - active={activeStep === 100} - > - 100% - - - + {showPercentage && ( + + { + if (disabled) return; + setAmountPercentage(25); + setActiveStep(25); + }} + active={activeStep === 25} + > + 25% + + { + if (disabled) return; + setAmountPercentage(50); + setActiveStep(50); + }} + active={activeStep === 50} + > + 50% + + { + if (disabled) return; + setAmountPercentage(75); + setActiveStep(75); + }} + active={activeStep === 75} + > + 75% + + { + if (disabled) return; + setAmountPercentage(100); + setActiveStep(100); + }} + active={activeStep === 100} + > + 100% + + + )} + ); }; @@ -169,7 +177,7 @@ const InputLabelAction = styled(GLink)` cursor: pointer; `; -const FiltersRow = styled(Flex)` +const PercentageRow = styled(Flex)` gap: 8px; `; diff --git a/src/components/AmountInput/StakingAmountInput.tsx b/src/components/AmountInput/StakingAmountInput.tsx new file mode 100644 index 0000000000..015d919647 --- /dev/null +++ b/src/components/AmountInput/StakingAmountInput.tsx @@ -0,0 +1,49 @@ +import { type FC } from 'react'; +import styled from 'styled-components'; +import { brandColors, neutralColors } from '@giveth/ui-design-system'; +import { StakingPlatform, type PoolStakingConfig } from '@/types/config'; +import { AmountInput, IAmountInput } from './AmountInput'; + +interface IStakingAmountInputProps extends IAmountInput { + poolStakingConfig: PoolStakingConfig; +} + +export const StakingAmountInput: FC = ({ + poolStakingConfig, + ...props +}) => { + return ( + + ); +}; + +const NumericalInput = styled(AmountInput)` + #amount-input { + width: 100%; + height: 54px; + padding: 15px 16px; + margin-top: 10px; + margin-bottom: 8px; + + background: ${brandColors.giv[700]}; + color: ${neutralColors.gray[100]}; + + border: 1px solid ${brandColors.giv[500]}; + border-radius: 8px; + + font-family: Red Hat Text; + font-style: normal; + font-weight: normal; + font-size: 16px; + line-height: 150%; + + ${props => (props.disabled ? `color: ${brandColors.giv[300]};` : '')} + } +`; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index eb8003e5e2..ad2255b9c6 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -53,6 +53,7 @@ import { isGIVeconomyRoute as checkIsGIVeconomyRoute } from '@/lib/helpers'; import { CommunityMenu } from '../menu/CommunityMenu'; import { useNavigationInfo } from '@/hooks/useNavigationInfo'; import config from '@/configuration'; +import { useShowHiderByScroll } from '@/hooks/useShowHiderByScroll'; import { useAuthenticationWallet } from '@/hooks/useAuthenticationWallet'; export interface IHeader { @@ -61,7 +62,6 @@ export interface IHeader { } const Header: FC = () => { - const [showHeader, setShowHeader] = useState(true); const [showBackBtn, setShowBackBtn] = useState(false); const [showSidebar, sidebarCondition, openSidebar, closeSidebar] = @@ -87,6 +87,7 @@ const Header: FC = () => { const { formatMessage } = useIntl(); const isDesktop = useMediaQuery(device.laptopL); const isMobile = useMediaQuery(device.mobileL); + const showHeader = useShowHiderByScroll(); const isGIVeconomyRoute = checkIsGIVeconomyRoute(router.route); @@ -128,36 +129,6 @@ const Header: FC = () => { ); }, [router.route]); - useEffect(() => { - const threshold = 0; - let lastScrollY = window.pageYOffset; - let ticking = false; - - const updateScrollDir = () => { - const scrollY = window.pageYOffset; - - if (Math.abs(scrollY - lastScrollY) < threshold) { - ticking = false; - return; - } - const show = scrollY <= lastScrollY; - setShowHeader(show); - lastScrollY = scrollY > 0 ? scrollY : 0; - ticking = false; - }; - - const onScroll = () => { - if (!ticking) { - window.requestAnimationFrame(updateScrollDir); - ticking = true; - } - }; - - window.addEventListener('scroll', onScroll); - - return () => window.removeEventListener('scroll', onScroll); - }, [showHeader]); - const handleModals = () => { openWalletConnectModal(); }; diff --git a/src/components/cards/MintCard.tsx b/src/components/cards/MintCard.tsx index b313406625..786e44326d 100644 --- a/src/components/cards/MintCard.tsx +++ b/src/components/cards/MintCard.tsx @@ -14,7 +14,7 @@ import { useAccount, useSwitchNetwork, useNetwork } from 'wagmi'; import { getContract } from 'wagmi/actions'; import { erc20ABI } from 'wagmi'; import { useWeb3Modal } from '@web3modal/wagmi/react'; -import { MintModal } from '../modals/MintModal'; +import { MintModal } from '../modals/Mint/MintModal'; import { Flex } from '../styled-components/Flex'; import { formatWeiHelper } from '@/helpers/number'; import config from '@/configuration'; @@ -58,8 +58,6 @@ export const MintCard = () => { } } - console.log('pfpData', pfpData); - useEffect(() => { async function fetchData() { try { diff --git a/src/components/input/index.tsx b/src/components/input/BaseInput.tsx similarity index 54% rename from src/components/input/index.tsx rename to src/components/input/BaseInput.tsx index 184971750c..a6a92b4192 100644 --- a/src/components/input/index.tsx +++ b/src/components/input/BaseInput.tsx @@ -1,42 +1,5 @@ -import React, { FC } from 'react'; +import { memo } from 'react'; import styled from 'styled-components'; -import { neutralColors, brandColors } from '@giveth/ui-design-system'; - -const InputContainer = styled.div` - background: #310bb5; - border-radius: 34px; - padding: 10px 10px 10px 32px; - height: 68px; - display: flex; - width: 100%; - align-items: center; - margin: 8px 0; -`; - -const Input = styled.input` - border: 0; - background: #310bb5; - color: white; - flex: 1; - font-size: 18px; - line-height: 160%; - ::placeholder { - color: white; - } - ::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } - ::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0; - } -`; - -const Unit = styled.span` - padding-right: 10px; - color: #cabaff; -`; const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`); // match escaped "." characters via in a non-capturing group @@ -45,7 +8,7 @@ function escapeRegExp(string: string): string { } // Shamelessly copied from Sushiswap front-end -const BaseInput = React.memo( +export const BaseInput = memo( ({ value, onUserInput, @@ -96,25 +59,11 @@ const BaseInput = React.memo( BaseInput.displayName = 'BaseInput'; -export const NumericalInput = styled(BaseInput)` - width: 100%; - height: 54px; - padding: 15px 16px; - margin-top: 10px; - margin-bottom: 8px; - - background: ${brandColors.giv[700]}; - color: ${neutralColors.gray[100]}; - - border: 1px solid ${brandColors.giv[500]}; - border-radius: 8px; - - font-family: Red Hat Text; - font-style: normal; - font-weight: normal; - font-size: 16px; - line-height: 150%; - +const Input = styled.input` + border: 0; + flex: 1; + font-size: 18px; + line-height: 160%; &:focus { outline: none; } @@ -126,33 +75,7 @@ export const NumericalInput = styled(BaseInput)` -webkit-appearance: none; margin: 0; } - ${props => (props.disabled ? `color: ${brandColors.giv[300]};` : '')} + ::placeholder { + color: white; + } `; - -interface IInputWithUnitProps { - placeholder?: string; - unit: string; - value: string; - onChange?: any; - type?: string; -} - -export const InputWithUnit: FC = ({ - placeholder, - unit, - value, - onChange, - type, -}) => { - return ( - - - {unit} - - ); -}; diff --git a/src/components/input/InputWithUnit.tsx b/src/components/input/InputWithUnit.tsx new file mode 100644 index 0000000000..0f4f9d2d79 --- /dev/null +++ b/src/components/input/InputWithUnit.tsx @@ -0,0 +1,47 @@ +import { type FC } from 'react'; +import styled from 'styled-components'; +import { BaseInput } from './BaseInput'; + +interface IInputWithUnitProps { + placeholder?: string; + unit: string; + value: string; + onChange?: any; + type?: string; +} + +export const InputWithUnit: FC = ({ + placeholder, + unit, + value, + onChange, + type, +}) => { + return ( + + + {unit} + + ); +}; + +const Unit = styled.span` + padding-right: 10px; + color: #cabaff; +`; + +const InputContainer = styled.div` + background: #310bb5; + border-radius: 34px; + padding: 10px 10px 10px 32px; + height: 68px; + display: flex; + width: 100%; + align-items: center; + margin: 8px 0; +`; diff --git a/src/components/modals/ConfirmSubmit.tsx b/src/components/modals/ConfirmSubmit.tsx index a0071dcc9c..89f8e3cbab 100644 --- a/src/components/modals/ConfirmSubmit.tsx +++ b/src/components/modals/ConfirmSubmit.tsx @@ -10,7 +10,7 @@ import { } from '@giveth/ui-design-system'; import styled from 'styled-components'; import { FC } from 'react'; -import { useNetwork } from 'wagmi'; +import { type Address, useNetwork } from 'wagmi'; import config from '@/configuration'; import TikAnimation from '@/animations/tik.json'; import ErrorAnimation from '@/animations/error.json'; @@ -18,7 +18,6 @@ import { AddTokenButton } from '../AddTokenButton'; import { Flex } from '../styled-components/Flex'; import LottieControl from '@/components/LottieControl'; import { WrappedSpinner } from '../Spinner'; -import { Address } from '@/types/config'; const AddTokenRow = styled(Flex)` margin-top: 16px; diff --git a/src/components/modals/ManageProjectAddresses/AddNewAddress.tsx b/src/components/modals/ManageProjectAddresses/AddNewAddress.tsx index 460225bbe0..c7ec530277 100644 --- a/src/components/modals/ManageProjectAddresses/AddNewAddress.tsx +++ b/src/components/modals/ManageProjectAddresses/AddNewAddress.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components'; import { useForm } from 'react-hook-form'; import { Dispatch, FC, SetStateAction, useState } from 'react'; import { getAddress, isAddress } from 'viem'; +import { type Address } from 'wagmi'; import { IProject, IWalletAddress } from '@/apollo/types/types'; import Input from '../../Input'; import { requiredOptions } from '@/lib/constants/regex'; @@ -10,7 +11,6 @@ import { client } from '@/apollo/apolloClient'; import { ADD_RECIPIENT_ADDRESS_TO_PROJECT } from '@/apollo/gql/gqlProjects'; import InlineToast, { EToastType } from '../../toasts/InlineToast'; import { suggestNewAddress } from '@/lib/helpers'; -import { Address } from '@/types/config'; import { getChainName } from '@/lib/network'; interface IAddNewAddress { diff --git a/src/components/modals/MintModal.tsx b/src/components/modals/Mint/MintModal.tsx similarity index 88% rename from src/components/modals/MintModal.tsx rename to src/components/modals/Mint/MintModal.tsx index 1fc4de1545..9dd6fe03ed 100644 --- a/src/components/modals/MintModal.tsx +++ b/src/components/modals/Mint/MintModal.tsx @@ -11,14 +11,8 @@ import { import { useAccount } from 'wagmi'; import { getWalletClient } from '@wagmi/core'; import { IModal } from '@/types/common'; -import { Modal } from './Modal'; +import { Modal } from '../Modal'; import { useModalAnimation } from '@/hooks/useModalAnimation'; -import { - StakeStepsContainer, - StakeStep, - StakeStepTitle, - StakeStepNumber, -} from './StakeLock/StakeSteps.sc'; import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; import { formatWeiHelper } from '@/helpers/number'; import { waitForTransaction } from '@/lib/transaction'; @@ -26,6 +20,7 @@ import { approveERC20tokenTransfer } from '@/lib/stakingPool'; import config from '@/configuration'; import { abi as PFP_ABI } from '@/artifacts/pfpGiver.json'; import { EPFPMinSteps, usePFPMintData } from '@/context/pfpmint.context'; +import { MintSteps } from './MintSteps'; export enum MintStep { APPROVE, APPROVING, @@ -137,23 +132,7 @@ export const MintModal: FC = ({ headerTitlePosition='left' > - - - - {' '} - {formatMessage({ id: 'label.approve' })} - - 1 - - - - {formatMessage({ id: 'label.mint' })} - - - 2 - - - + You are Minting {qty} Giver NFT {qty > 1 && 's'} for{' '} diff --git a/src/components/modals/Mint/MintSteps.tsx b/src/components/modals/Mint/MintSteps.tsx new file mode 100644 index 0000000000..4f809d50cc --- /dev/null +++ b/src/components/modals/Mint/MintSteps.tsx @@ -0,0 +1,17 @@ +import { type FC } from 'react'; +import { Steps } from '@/components/steps/Steps'; +import { MintStep } from './MintModal'; + +interface IMintStepsProps { + mintState: MintStep; +} + +const steps = ['label.approve', 'label.mint']; + +export const MintSteps: FC = ({ mintState }) => { + let activeStep = 0; + if (mintState === MintStep.MINT || mintState === MintStep.MINTING) { + activeStep = 1; + } + return ; +}; diff --git a/src/components/modals/StakeLock/Lock.tsx b/src/components/modals/StakeLock/Lock.tsx index 37cb402c90..53a9df6057 100644 --- a/src/components/modals/StakeLock/Lock.tsx +++ b/src/components/modals/StakeLock/Lock.tsx @@ -21,7 +21,6 @@ import { StakeInnerModalContainer, StakeModalContainer, } from './StakeLock.sc'; -import { AmountInput } from '@/components/AmountInput'; import LockSlider from './LockSlider'; import LockInfo, { LockInfoTooltip } from './LockInfo'; import LockingBrief from './LockingBrief'; @@ -38,6 +37,7 @@ import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; import { useStakingPool } from '@/hooks/useStakingPool'; import { useTokenDistroHelper } from '@/hooks/useTokenDistroHelper'; import TotalGIVpowerBox from './TotalGIVpowerBox'; +import { StakingAmountInput } from '@/components/AmountInput/StakingAmountInput'; import type { PoolStakingConfig } from '@/types/config'; interface ILockModalProps extends IModal { @@ -134,7 +134,7 @@ const LockModal: FC = ({ id: 'label.lock_your_staked_giv', })} - = ({ - = ({ id: 'label.amount_to_stake', })} - ` - margin-bottom: 8px; - color: ${props => - props.disable ? brandColors.giv[300] : brandColors.giv['000']}; -`; -export const StakeStepNumber = styled(SublineBold)` - color: ${props => - props.disable ? brandColors.giv[300] : brandColors.giv['000']}; - background-color: ${brandColors.giv[500]}; - border: 3px solid - ${props => - props.disable ? brandColors.giv[300] : brandColors.giv['000']}; - border-radius: 18px; - width: 24px; -`; - -export const StakeStepsPlaceholder = styled.div` - padding: 13px; -`; diff --git a/src/components/modals/StakeLock/StakeSteps.tsx b/src/components/modals/StakeLock/StakeSteps.tsx index 1a5824fbe1..5c8b8b20d1 100644 --- a/src/components/modals/StakeLock/StakeSteps.tsx +++ b/src/components/modals/StakeLock/StakeSteps.tsx @@ -1,49 +1,22 @@ -import React from 'react'; +import { type FC } from 'react'; +import { Steps } from '@/components/steps/Steps'; import { StakeState } from '@/lib/staking'; -import { - StakeStep, - StakeStepTitle, - StakeStepNumber, - StakeStepsPlaceholder, - StakeStepsContainer, -} from './StakeSteps.sc'; -const StakeSteps = ({ stakeState }: { stakeState: StakeState }) => { - return stakeState === StakeState.APPROVE || - stakeState === StakeState.APPROVING || +interface IStakeStepsProps { + stakeState: StakeState; +} + +const steps = ['label.approve', 'label.stake']; + +export const StakeSteps: FC = ({ stakeState }) => { + let activeStep = 0; + if ( stakeState === StakeState.WRAP || - stakeState === StakeState.WRAPPING ? ( - - - Approve - 1 - - - - Stake - - - 2 - - - - ) : ( - - ); + stakeState === StakeState.WRAPPING || + stakeState === StakeState.STAKE || + stakeState === StakeState.STAKING + ) { + activeStep = 1; + } + return ; }; - -export default StakeSteps; diff --git a/src/components/modals/Unstake/UnStake.tsx b/src/components/modals/Unstake/UnStake.tsx index 791f120867..3017771112 100644 --- a/src/components/modals/Unstake/UnStake.tsx +++ b/src/components/modals/Unstake/UnStake.tsx @@ -12,7 +12,6 @@ import { useNetwork } from 'wagmi'; import { Modal } from '../Modal'; import { Flex } from '../../styled-components/Flex'; import { StakingPoolImages } from '../../StakingPoolImages'; -import { AmountInput } from '../../AmountInput'; import { unwrapToken, withdrawTokens } from '@/lib/stakingPool'; import { ConfirmedInnerModal, @@ -34,6 +33,7 @@ import { mediaQueries } from '@/lib/constants/constants'; import { useModalAnimation } from '@/hooks/useModalAnimation'; import { useStakingPool } from '@/hooks/useStakingPool'; import { useTokenDistroHelper } from '@/hooks/useTokenDistroHelper'; +import { StakingAmountInput } from '@/components/AmountInput/StakingAmountInput'; import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; interface IUnStakeInnerModalProps { @@ -129,7 +129,7 @@ const UnStakeInnerModal: FC = ({ - = ({ steps, activeStep }) => { + const { formatMessage } = useIntl(); + const theme = useAppSelector(state => state.general.theme); + + return ( + + {steps.map((step, index) => ( + + activeStep} theme={theme}> + {formatMessage({ id: step })} + + activeStep} theme={theme}> + {index + 1} + + + ))} + + ); +}; + +const StepsContainer = styled(Flex)` + position: relative; + justify-content: space-evenly; + &::before { + content: ''; + position: absolute; + width: 100%; + height: 1px; + border-top: 1px solid + ${props => + props.theme === ETheme.Dark + ? brandColors.giv[500] + : brandColors.giv[100]}; + bottom: 11px; + z-index: 0; + } + &::after { + content: ''; + position: absolute; + height: 1px; + border-top: 1px dashed + ${props => + props.theme === ETheme.Dark + ? brandColors.giv[500] + : brandColors.giv[100]}; + left: -24px; + right: -24px; + bottom: 11px; + z-index: 0; + } + margin-bottom: 16px; +`; + +const Step = styled(Flex)` + flex-direction: column; + justify-content: center; + align-items: center; + width: 61px; + position: relative; + z-index: 1; + user-select: none; +`; + +interface IStepState { + disable?: boolean; +} + +const StepTitle = styled(P)` + margin-bottom: 8px; + color: ${props => + props.disable + ? props.theme === ETheme.Dark + ? brandColors.giv[300] + : brandColors.giv[200] + : props.theme === ETheme.Dark + ? brandColors.giv['000'] + : brandColors.giv[500]}; +`; +const StepNumber = styled(SublineBold)` + color: ${props => + props.disable + ? props.theme === ETheme.Dark + ? brandColors.giv[200] + : neutralColors.gray[100] + : props.theme === ETheme.Dark + ? brandColors.giv['000'] + : neutralColors.gray[100]}; + background-color: ${props => + props.disable + ? props.theme === ETheme.Dark + ? brandColors.giv[500] + : brandColors.giv[200] + : brandColors.giv[500]}; + border: 3px solid + ${props => + props.disable + ? props.theme === ETheme.Dark + ? brandColors.giv[300] + : brandColors.giv[100] + : props.theme === ETheme.Dark + ? brandColors.giv['000'] + : brandColors.giv[100]}; + border-radius: 18px; + width: 24px; +`; + +const StepsPlaceholder = styled.div` + padding: 13px; +`; diff --git a/src/components/views/claim/cards/Donate.tsx b/src/components/views/claim/cards/Donate.tsx index 06de37bbe1..a3fd5b82ef 100644 --- a/src/components/views/claim/cards/Donate.tsx +++ b/src/components/views/claim/cards/Donate.tsx @@ -34,10 +34,10 @@ import { IClaimViewCardProps } from '@/components/views/claim/Claim.view'; import useClaim from '@/context/claim.context'; import useGIVTokenDistroHelper from '@/hooks/useGIVTokenDistroHelper'; import { IconWithTooltip } from '@/components/IconWithToolTip'; -import { InputWithUnit } from '@/components/input/index'; import { Flex } from '@/components/styled-components/Flex'; import { formatWeiHelper } from '@/helpers/number'; import { WeiPerEther } from '@/lib/constants/constants'; +import { InputWithUnit } from '@/components/input/InputWithUnit'; const DonatePoolCard = styled(PoolCard)` height: 127px; diff --git a/src/components/views/claim/cards/Govern.tsx b/src/components/views/claim/cards/Govern.tsx index 6f52237c82..08f5bd395b 100644 --- a/src/components/views/claim/cards/Govern.tsx +++ b/src/components/views/claim/cards/Govern.tsx @@ -32,10 +32,10 @@ import { APR } from '@/types/poolInfo'; import useClaim from '@/context/claim.context'; import { useAppSelector } from '@/features/hooks'; import useGIVTokenDistroHelper from '@/hooks/useGIVTokenDistroHelper'; -import { InputWithUnit } from '@/components/input/index'; import { Flex } from '@/components/styled-components/Flex'; import { IClaimViewCardProps } from '../Claim.view'; import { WeiPerEther } from '@/lib/constants/constants'; +import { InputWithUnit } from '@/components/input/InputWithUnit'; const GovernCardContainer = styled(Card)` padding-left: 254px; diff --git a/src/components/views/claim/cards/Stake.tsx b/src/components/views/claim/cards/Stake.tsx index bb5740b228..7f9d1770e3 100644 --- a/src/components/views/claim/cards/Stake.tsx +++ b/src/components/views/claim/cards/Stake.tsx @@ -30,10 +30,10 @@ import useGIVTokenDistroHelper from '@/hooks/useGIVTokenDistroHelper'; import { useAppSelector } from '@/features/hooks'; import { SimplePoolStakingConfig, StakingType } from '@/types/config'; import { getNowUnixMS } from '@/helpers/time'; -import { InputWithUnit } from '@/components/input/index'; import { Flex } from '@/components/styled-components/Flex'; import { IClaimViewCardProps } from '../Claim.view'; import { WeiPerEther } from '@/lib/constants/constants'; +import { InputWithUnit } from '@/components/input/InputWithUnit'; const InvestCardContainer = styled(Card)` ::before { diff --git a/src/components/views/create/WalletAddressInput.tsx b/src/components/views/create/WalletAddressInput.tsx index 6c54c36fc8..4a992269dc 100644 --- a/src/components/views/create/WalletAddressInput.tsx +++ b/src/components/views/create/WalletAddressInput.tsx @@ -11,7 +11,7 @@ import { import styled from 'styled-components'; import { useFormContext } from 'react-hook-form'; import { isAddress } from 'viem'; -import { useNetwork } from 'wagmi'; +import { type Address, useNetwork } from 'wagmi'; import { compareAddresses, findAddressByChain } from '@/lib/helpers'; import { useAppSelector } from '@/features/hooks'; import Input, { InputSize } from '@/components/Input'; @@ -39,7 +39,7 @@ const WalletAddressInput: FC = ({ onSubmit, chainType, }) => { - const [resolvedENS, setResolvedENS] = useState<`0x${string}` | undefined>(); + const [resolvedENS, setResolvedENS] = useState
(); const { getValues, setValue } = useFormContext(); const { chain } = useNetwork(); @@ -109,7 +109,7 @@ const WalletAddressInput: FC = ({ if (!address || address.length === 0) { return formatMessage({ id: 'label.this_field_is_required' }); } - let _address = (' ' + address).slice(1) as `0x${string}`; + let _address = (' ' + address).slice(1) as Address; setIsValidating(true); if (isAddressENS(address)) { _address = await ENSHandler(address); diff --git a/src/components/views/donate/CryptoDonation.tsx b/src/components/views/donate/CryptoDonation.tsx index 8552487cf5..9db43d2c47 100644 --- a/src/components/views/donate/CryptoDonation.tsx +++ b/src/components/views/donate/CryptoDonation.tsx @@ -5,7 +5,6 @@ import { brandColors, Button, GLink, - H4, neutralColors, semanticColors, } from '@giveth/ui-design-system'; @@ -14,12 +13,18 @@ import { captureException } from '@sentry/nextjs'; import { formatUnits, parseUnits } from 'viem'; import { getContract } from 'wagmi/actions'; -import { erc20ABI, useAccount, useBalance, useNetwork } from 'wagmi'; +import { + type Address, + erc20ABI, + useAccount, + useBalance, + useNetwork, +} from 'wagmi'; import { useWeb3Modal } from '@web3modal/wagmi/react'; import { Shadow } from '@/components/styled-components/Shadow'; import InputBox from './InputBox'; import CheckBox from '@/components/Checkbox'; -import DonateModal from '@/components/modals/DonateModal'; +import DonateModal from '@/components/views/donate/DonateModal'; import { mediaQueries, minDonationAmount } from '@/lib/constants/constants'; import { InsufficientFundModal } from '@/components/modals/InsufficientFund'; import GeminiModal from './GeminiModal'; @@ -120,7 +125,6 @@ const CryptoDonation: FC = () => { const totalDonation = ((amountTyped || 0) * (donationToGiveth + 100)) / 100; const activeRound = getActiveRound(project.qfRounds); const networkId = chain?.id; - const isOnAcceptedChain = networkId && acceptedChains?.includes(networkId); const isOnEligibleNetworks = networkId && activeRound?.eligibleNetworks?.includes(networkId); @@ -214,7 +218,7 @@ const CryptoDonation: FC = () => { request: async () => { try { const contract = getContract({ - address: selectedToken.address! as `0x${string}`, + address: selectedToken.address! as Address, abi: erc20ABI, }); @@ -242,7 +246,7 @@ const CryptoDonation: FC = () => { )(); }, [address, networkId, tokenSymbol, balance]); - const handleCustomToken = (i: `0x${string}`) => { + const handleCustomToken = (i: Address) => { if (!supportCustomTokens) return; // It's a contract if (i?.length === 42) { @@ -318,9 +322,6 @@ const CryptoDonation: FC = () => { return ( - - {formatMessage({ id: 'page.donate.title' })} - {showQFModal && ( { setDonationToGiveth(e); }} donationToGiveth={donationToGiveth} + title={ + formatMessage({ id: 'label.donation_to' }) + ' Giveth' + } /> ) : (
@@ -491,10 +495,6 @@ const CryptoDonation: FC = () => { ); }; -const H4Styled = styled(H4)` - margin-bottom: 30px; -`; - const EmptySpace = styled.div` margin-top: 70px; `; @@ -504,6 +504,7 @@ const MainContainer = styled.div` flex-direction: column; height: 60%; justify-content: space-between; + text-align: left; `; const InputContainer = styled.div` diff --git a/src/components/views/donate/DonateHeader.tsx b/src/components/views/donate/DonateHeader.tsx new file mode 100644 index 0000000000..c116622b61 --- /dev/null +++ b/src/components/views/donate/DonateHeader.tsx @@ -0,0 +1,86 @@ +import Image from 'next/image'; +import { FC } from 'react'; +import { useRouter } from 'next/router'; +import { useIntl } from 'react-intl'; +import { useAccount, useNetwork } from 'wagmi'; +import { useWeb3Modal } from '@web3modal/wagmi/react'; +import Link from 'next/link'; +import { Caption, B, neutralColors } from '@giveth/ui-design-system'; +import styled from 'styled-components'; +import { Flex, FlexSpacer } from '@/components/styled-components/Flex'; +import { useAppSelector } from '@/features/hooks'; +import { ETheme } from '@/features/general/general.slice'; + +import { isGIVeconomyRoute as checkIsGIVeconomyRoute } from '@/lib/helpers'; +import { + StyledHeader, + Logo, + ConnectButton, +} from '@/components/Header/Header.sc'; +import { UserButtonWithMenu } from '@/components/menu/UserButtonWithMenu'; +import Routes from '@/lib/constants/Routes'; +import { useDonateData } from '@/context/donate.context'; +import { useShowHiderByScroll } from '@/hooks/useShowHiderByScroll'; + +export interface IHeader { + theme?: ETheme; + show?: boolean; +} + +export const DonateHeader: FC = () => { + const theme = useAppSelector(state => state.general.theme); + const { formatMessage } = useIntl(); + const { address } = useAccount(); + const { chain } = useNetwork(); + const router = useRouter(); + const { open: openConnectModal } = useWeb3Modal(); + const { project } = useDonateData(); + const showHeader = useShowHiderByScroll(); + const chainId = chain?.id; + const isGIVeconomyRoute = checkIsGIVeconomyRoute(router.route); + + return ( + + + + + Giveth logo + + + + Donating to + {project.title} + + + + + {address && chainId ? ( + + ) : ( + openConnectModal?.()} + /> + )} + + + ); +}; + +const StyledCaption = styled(Caption)` + color: ${neutralColors.gray[700]}; +`; diff --git a/src/components/views/donate/DonateIndex.tsx b/src/components/views/donate/DonateIndex.tsx index 99458bb934..20818bef30 100644 --- a/src/components/views/donate/DonateIndex.tsx +++ b/src/components/views/donate/DonateIndex.tsx @@ -1,24 +1,17 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect } from 'react'; import styled from 'styled-components'; import { - brandColors, - ButtonLink, + Col, + Container, IconDonation24, - IconExternalLink24, - Lead, neutralColors, + Row, semanticColors, SublineBold, } from '@giveth/ui-design-system'; -import Link from 'next/link'; import { useIntl } from 'react-intl'; -import dynamic from 'next/dynamic'; -import { useNetwork } from 'wagmi'; import { BigArc } from '@/components/styled-components/Arc'; -import { mediaQueries } from '@/lib/constants/constants'; import SocialBox from '../../DonateSocialBox'; -import SuccessView from '@/components/views/donate/SuccessView'; -import ProjectCardSelector from '@/components/views/donate/ProjectCardSelector'; import NiceBanner from './NiceBanner'; // import PurchaseXDAI from './PurchaseXDAIBanner'; import useDetectDevice from '@/hooks/useDetectDevice'; @@ -26,35 +19,46 @@ import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; import { useDonateData } from '@/context/donate.context'; import { EContentType } from '@/lib/constants/shareContent'; import { PassportBanner } from '@/components/PassportBanner'; -import ExternalLink from '@/components/ExternalLink'; -import { formatTxLink } from '@/lib/helpers'; -import Routes from '@/lib/constants/Routes'; -import { Flex, FlexCenter } from '@/components/styled-components/Flex'; +import { Flex } from '@/components/styled-components/Flex'; import { useAlreadyDonatedToProject } from '@/hooks/useAlreadyDonatedToProject'; import { Shadow } from '@/components/styled-components/Shadow'; - -const CryptoDonation = dynamic( - () => import('@/components/views/donate/CryptoDonation'), - { - loading: () =>

Loading...

, - ssr: false, - }, -); +import { useAppDispatch } from '@/features/hooks'; +import { setShowHeader } from '@/features/general/general.slice'; +import { DonateHeader } from './DonateHeader'; +import { DonationCard } from './DonationCard'; +import { SuccessView } from './SuccessView'; +import { DonateSection } from '../project/projectActionCard/DonationSection'; +import QFSection from '../project/projectActionCard/QFSection'; +import ProjectCardImage from '@/components/project-card/ProjectCardImage'; const DonateIndex: FC = () => { const { formatMessage } = useIntl(); const { isMobile } = useDetectDevice(); const { project, isSuccessDonation, hasActiveQFRound } = useDonateData(); const alreadyDonated = useAlreadyDonatedToProject(project); - const { txHash = [] } = isSuccessDonation || {}; - const hasMultipleTxs = txHash.length > 1; + const dispatch = useAppDispatch(); const isSafeEnv = useIsSafeEnvironment(); - return ( + useEffect(() => { + dispatch(setShowHeader(false)); + return () => { + dispatch(setShowHeader(true)); + }; + }, [dispatch]); + + return isSuccessDonation ? ( + <> + + + + + + ) : ( <> + {!isSafeEnv && hasActiveQFRound && } - + {/* */} {alreadyDonated && ( @@ -67,53 +71,37 @@ const DonateIndex: FC = () => { )} - - - - {isSuccessDonation ? ( - - ) : ( - - )} - - - {isSuccessDonation && ( - - - {formatMessage({ - id: 'label.your_transactions_have_been_submitted', - })} -
- {formatMessage({ - id: 'label.you_can_view_them_on_a_blockchain_explorer_here', - })} -
- - {hasMultipleTxs && ( - - )} - - - -
- )} - {!isSuccessDonation && !isMobile && ( + + + + + + + + + + {!isMobile && hasActiveQFRound ? ( + + ) : ( + + )} + + + + {!isMobile && ( )} -
+ ); }; const AlreadyDonatedWrapper = styled(Flex)` - margin: 0 40px 16px 40px; + margin-bottom: 16px; padding: 12px 16px; gap: 8px; color: ${semanticColors.jade[500]}; @@ -123,75 +111,28 @@ const AlreadyDonatedWrapper = styled(Flex)` align-items: center; `; -const TxRow = ({ txHash, title }: { txHash: string; title?: string }) => { - const { chain } = useNetwork(); - const chainId = chain?.id; - return ( - - Donation to {title + ' '} - - - - ); -}; - -const TxLink = styled(Lead)` - color: ${brandColors.pinky[500]}; - cursor: pointer; - margin-top: 16px; - display: flex; - align-items: center; - gap: 8px; - > span { - color: ${neutralColors.gray[700]}; - } -`; - -const Options = styled(FlexCenter)` - flex-direction: column; - width: 100%; - padding: 40px 20px 0; -`; - -const ProjectsButton = styled(ButtonLink)` - width: 242px; - margin-top: 40px; -`; - -const Wrapper = styled.div` - max-width: 1052px; +const DonateContainer = styled(Container)` text-align: center; - padding: 64px 0; - margin: 0 auto; + padding-top: 128px; + padding-bottom: 64px; position: relative; `; -const Sections = styled.div` +const InfoWrapper = styled.div` + background-color: ${neutralColors.gray[100]}; + padding: 24px; + border-radius: 16px; height: 100%; - ${mediaQueries.tablet} { - display: grid; - grid-template-columns: repeat(2, minmax(500px, 1fr)); - grid-auto-rows: minmax(100px, auto); - } - ${mediaQueries.mobileL} { - grid-template-columns: repeat(2, minmax(100px, 1fr)); - padding: 0 40px; - } + text-align: left; `; -const Right = styled.div` - z-index: 1; - background: white; - text-align: left; - padding: 32px; - min-height: 620px; - border-radius: 16px; - ${mediaQueries.tablet} { - border-radius: 0 16px 16px 0; - } +const ImageWrapper = styled.div` + position: relative; + width: 100%; + height: 231px; + margin-bottom: 24px; + border-radius: 8px; + overflow: hidden; `; export default DonateIndex; diff --git a/src/components/modals/DonateModal.tsx b/src/components/views/donate/DonateModal.tsx similarity index 83% rename from src/components/modals/DonateModal.tsx rename to src/components/views/donate/DonateModal.tsx index 3f0ab46522..b74b17274f 100644 --- a/src/components/modals/DonateModal.tsx +++ b/src/components/views/donate/DonateModal.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect, useState } from 'react'; +import React, { FC, useState } from 'react'; import styled from 'styled-components'; import { brandColors, @@ -8,7 +8,6 @@ import { Button, } from '@giveth/ui-design-system'; import { useIntl } from 'react-intl'; -import BigNumber from 'bignumber.js'; import { useAccount, useNetwork } from 'wagmi'; import StorageLabel, { getWithExpiry } from '@/lib/localStorage'; @@ -23,7 +22,7 @@ import FailedDonation, { } from '@/components/modals/FailedDonation'; import { client } from '@/apollo/apolloClient'; import { VALIDATE_TOKEN } from '@/apollo/gql/gqlUser'; -import { useAppDispatch, useAppSelector } from '@/features/hooks'; +import { useAppDispatch } from '@/features/hooks'; import { signOut } from '@/features/user/user.thunks'; import { setShowSignWithWallet } from '@/features/modal/modal.slice'; import { useModalAnimation } from '@/hooks/useModalAnimation'; @@ -32,9 +31,8 @@ import DonateSummary from '@/components/views/donate/DonateSummary'; import ExternalLink from '@/components/ExternalLink'; import InlineToast, { EToastType } from '@/components/toasts/InlineToast'; import { useDonateData } from '@/context/donate.context'; -import { fetchETCPrice, fetchPrice } from '@/services/token'; -import { fetchEthPrice } from '@/features/price/price.services'; import { useCreateDonation } from '@/hooks/useCreateDonation'; +import { useTokenPrice } from '@/hooks/useTokenPrice'; interface IDonateModalProps extends IModal { token: IProjectAcceptedToken; @@ -45,14 +43,6 @@ interface IDonateModalProps extends IModal { givBackEligible?: boolean; } -const ethereumChain = config.MAINNET_CONFIG; -const gnosisChain = config.GNOSIS_CONFIG; -const stableCoins = [ - gnosisChain.nativeCurrency.symbol.toUpperCase(), - 'DAI', - 'USDT', -]; - const DonateModal: FC = props => { const { token, @@ -82,17 +72,14 @@ const DonateModal: FC = props => { const { formatMessage } = useIntl(); const { setSuccessDonation, project } = useDonateData(); - const givPrice = useAppSelector(state => state.price.givPrice); - const givTokenPrice = new BigNumber(givPrice).toNumber(); - const isMainnet = chainId === config.MAINNET_NETWORK_NUMBER; - const [donating, setDonating] = useState(false); const [secondTxStatus, setSecondTxStatus] = useState(); const [processFinished, setProcessFinished] = useState(false); - const [tokenPrice, setTokenPrice] = useState(); const [failedModalType, setFailedModalType] = useState(); + const tokenPrice = useTokenPrice(token); + const chainvineReferred = getWithExpiry(StorageLabel.CHAINVINEREFERRED); const { title, addresses, givethAddresses } = project || {}; const projectWalletAddress = addresses?.find( @@ -203,49 +190,6 @@ const DonateModal: FC = props => { .catch(console.log); }; - useEffect(() => { - const setPrice = async () => { - if ( - token?.symbol && - stableCoins.includes(token.symbol.toUpperCase()) - ) { - setTokenPrice(1); - } else if (token?.symbol === 'GIV') { - setTokenPrice(givTokenPrice || 0); - } else if (token?.symbol === ethereumChain.nativeCurrency.symbol) { - const ethPrice = await fetchEthPrice(); - setTokenPrice(ethPrice || 0); - } else if (token?.address) { - let tokenAddress = token.address; - // Coingecko doesn't have these tokens in Gnosis Chain, so fetching price from ethereum - if (!isMainnet && token.mainnetAddress) { - tokenAddress = - (token.mainnetAddress as `0x${string}`) || - ('' as `0x${string}`); - } - // ETC is not supported by coingecko with contract address, so we should use this function to fetch the price - if (token.symbol === 'ETC') { - const fetchedETCPrice = await fetchETCPrice(); - setTokenPrice(fetchedETCPrice || 0); - return; - } - const coingeckoChainId = - isMainnet || - (token.mainnetAddress && token.symbol !== 'CELO') - ? config.MAINNET_NETWORK_NUMBER - : chainId!; - const fetchedPrice = await fetchPrice( - coingeckoChainId, - tokenAddress, - ); - setTokenPrice(fetchedPrice || 0); - } - }; - if (token) { - setPrice().catch(() => setTokenPrice(0)); - } - }, [token]); - if (!projectWalletAddress) { showToastError('There is no eth address assigned for this project'); return null; diff --git a/src/components/views/donate/DonateToGiveth.tsx b/src/components/views/donate/DonateToGiveth.tsx index db4104d92d..07826bf75d 100644 --- a/src/components/views/donate/DonateToGiveth.tsx +++ b/src/components/views/donate/DonateToGiveth.tsx @@ -19,13 +19,16 @@ import CheckBox from '@/components/Checkbox'; interface IDonateToGiveth { donationToGiveth: number; setDonationToGiveth: (donationToGiveth: number) => void; + title: string; } const givethDonationOptions = [5, 10, 15, 20]; -const DonateToGiveth: FC = props => { - const { donationToGiveth, setDonationToGiveth } = props; - +const DonateToGiveth: FC = ({ + donationToGiveth, + setDonationToGiveth, + title, +}) => { const { formatMessage } = useIntl(); const handleChange = (e: ChangeEvent) => { @@ -42,9 +45,7 @@ const DonateToGiveth: FC = props => { return ( - - {formatMessage({ id: 'label.donation_to' }) + ' Giveth'} - + {title} } direction='top'> {formatMessage({ id: 'label.support_giveth_with' })} diff --git a/src/components/views/donate/DonationCard.tsx b/src/components/views/donate/DonationCard.tsx new file mode 100644 index 0000000000..71287da3f0 --- /dev/null +++ b/src/components/views/donate/DonationCard.tsx @@ -0,0 +1,108 @@ +import { B, P, neutralColors } from '@giveth/ui-design-system'; +import { useState } from 'react'; +import styled, { css } from 'styled-components'; +import { useIntl } from 'react-intl'; +import { Shadow } from '@/components/styled-components/Shadow'; +import { Flex, FlexCenter } from '@/components/styled-components/Flex'; +import { RecurringDonationCard } from './RecurringDonationCard'; +import CryptoDonation from './CryptoDonation'; + +export const isRecurringActive = + process.env.NEXT_PUBLIC_RECURRING_DONATION === 'true'; + +enum ETabs { + ONE_TIME, + RECURRING, +} + +const tabs = ['label.one_time_donation', 'label.recurring_donation']; + +export const DonationCard = () => { + const [tab, setTab] = useState(ETabs.ONE_TIME); + const { formatMessage } = useIntl(); + return ( + + + {formatMessage({ id: 'label.how_do_you_want_to_donate' })} + + + {tabs.map((_tab, idx) => ( + setTab(idx)} + > + {formatMessage({ + id: _tab, + })} + + ))} + + + + {tab === ETabs.ONE_TIME && } + {tab === ETabs.RECURRING && + (isRecurringActive ? ( + + ) : ( + + {formatMessage({ + id: 'label.this_feature_will_be_available_soon', + })} + + ))} + + + ); +}; + +const DonationCardWrapper = styled(Flex)` + flex-direction: column; + gap: 16px; + padding: 24px; + border-radius: 16px; + align-items: flex-start; + background: ${neutralColors.gray[100]}; + box-shadow: ${Shadow.Neutral[400]}; + align-items: stretch; + height: 100%; +`; + +const Title = styled(B)` + color: ${neutralColors.gray[800]}; + text-align: left; +`; + +interface ITab { + selected?: boolean; +} + +const Tab = styled(P)` + padding: 8px 12px; + border-bottom: 1px solid; + cursor: pointer; + ${props => + props.selected + ? css` + font-weight: 500; + color: ${neutralColors.gray[900]}; + border-bottom-color: ${neutralColors.gray[900]}; + ` + : css` + font-weight: 400; + color: ${neutralColors.gray[700]}; + border-bottom-color: ${neutralColors.gray[300]}; + `} +`; + +const EmptyTab = styled.div` + flex: 1; + border-bottom: 1px solid ${neutralColors.gray[300]}; +`; + +const TabWrapper = styled(Flex)` + position: relative; + flex-direction: column; + gap: 16px; + align-items: flex-start; +`; diff --git a/src/components/views/donate/EstimatedMatchingToast.tsx b/src/components/views/donate/EstimatedMatchingToast.tsx index 00548d5e77..9b93e5c6fa 100644 --- a/src/components/views/donate/EstimatedMatchingToast.tsx +++ b/src/components/views/donate/EstimatedMatchingToast.tsx @@ -10,7 +10,7 @@ import { import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import BigNumber from 'bignumber.js'; -import { useNetwork } from 'wagmi'; +import { type Address, useNetwork } from 'wagmi'; import Divider from '@/components/Divider'; import { TooltipContent } from '@/components/modals/HarvestAll.sc'; import { IconWithTooltip } from '@/components/IconWithToolTip'; @@ -72,7 +72,7 @@ const EstimatedMatchingToast = ({ const ethPrice = await fetchEthPrice(); setTokenPrice(ethPrice || 0); } else if (token?.address) { - let tokenAddress = token.address as `0x${string}`; + let tokenAddress = token.address as Address; // Coingecko doesn't have these tokens in Gnosis Chain, so fetching price from ethereum if (!isMainnet && token.mainnetAddress) { tokenAddress = token.mainnetAddress || ''; diff --git a/src/components/views/donate/ModifySuperToken/DepositSuperToken.tsx b/src/components/views/donate/ModifySuperToken/DepositSuperToken.tsx new file mode 100644 index 0000000000..626619ed84 --- /dev/null +++ b/src/components/views/donate/ModifySuperToken/DepositSuperToken.tsx @@ -0,0 +1,387 @@ +import { useState, type FC, useMemo, useEffect } from 'react'; +import styled from 'styled-components'; +import { + Caption, + IconHelpFilled16, + B, + GLink, + IconRefresh16, + neutralColors, + brandColors, + Button, +} from '@giveth/ui-design-system'; + +import { useAccount, useBalance } from 'wagmi'; +import { useIntl } from 'react-intl'; +import { Framework } from '@superfluid-finance/sdk-core'; +import { Flex } from '@/components/styled-components/Flex'; +import { FlowRateTooltip } from '@/components/GIVeconomyPages/GIVstream.sc'; +import { IconWithTooltip } from '@/components/IconWithToolTip'; +import { Spinner } from '@/components/Spinner'; +import { TokenIcon } from '../TokenIcon/TokenIcon'; +import { ISuperToken, IToken } from '@/types/superFluid'; +import { AddressZero, ONE_MONTH_SECONDS } from '@/lib/constants/constants'; +import { AmountInput } from '@/components/AmountInput/AmountInput'; +import { findSuperTokenByTokenAddress } from '@/helpers/donate'; +import { ITokenStreams } from '@/context/donate.context'; +import { ModifyInfoToast } from './ModifyInfoToast'; +import { + EModifySuperTokenSteps, + IModifySuperTokenInnerModalProps, + actionButtonLabel, +} from './ModifySuperTokenModal'; +import { DepositSteps } from './DepositSuperTokenSteps'; +import { Item } from '../RecurringDonationModal/Item'; +import { useTokenPrice } from '@/hooks/useTokenPrice'; +import { RunOutInfo } from '../RunOutInfo'; +import { approveERC20tokenTransfer } from '@/lib/stakingPool'; +import config from '@/configuration'; +import { getEthersProvider, getEthersSigner } from '@/helpers/ethers'; +import { showToastError } from '@/lib/helpers'; +import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; + +interface IDepositSuperTokenProps extends IModifySuperTokenInnerModalProps { + tokenStreams: ITokenStreams; + selectedToken: IToken; +} + +export const DepositSuperToken: FC = ({ + selectedToken, + tokenStreams, + step, + setStep, + setShowModal, +}) => { + const [amount, setAmount] = useState(0n); + + const { address } = useAccount(); + const { formatMessage } = useIntl(); + + const [token, superToken] = useMemo( + () => + selectedToken.isSuperToken + ? [selectedToken.underlyingToken, selectedToken as ISuperToken] + : [ + selectedToken, + findSuperTokenByTokenAddress(selectedToken.id), + ], + [selectedToken], + ); + + const { + data: balance, + refetch, + isRefetching, + } = useBalance({ + token: token?.id === AddressZero ? undefined : token?.id, + address: address, + }); + + const { data: SuperTokenBalance } = useBalance({ + token: superToken?.id, + address: address, + }); + + const tokenPrice = useTokenPrice(token); + const isSafeEnv = useIsSafeEnvironment(); + + useEffect(() => { + if (!token) return; + if (step === EModifySuperTokenSteps.APPROVE && token.symbol === 'ETH') { + setStep(EModifySuperTokenSteps.DEPOSIT); + } + }, [token, setStep, step]); + + const tokenStream = tokenStreams[superToken?.id || '']; + const totalStreamPerSec = + tokenStream?.reduce( + (acc, stream) => acc + BigInt(stream.currentFlowRate), + 0n, + ) || 0n; + const streamRunOutInMonth = + SuperTokenBalance !== undefined && + totalStreamPerSec > 0 && + SuperTokenBalance.value > 0n + ? SuperTokenBalance.value / totalStreamPerSec / ONE_MONTH_SECONDS + : 0n; + + const isLoading = + step === EModifySuperTokenSteps.APPROVING || + step === EModifySuperTokenSteps.DEPOSITING; + + const onApprove = async () => { + console.log('Approve', amount, address, superToken, token); + if (!address || !superToken || !token) return; + setStep(EModifySuperTokenSteps.APPROVING); + try { + const approve = await approveERC20tokenTransfer( + amount, + address, + superToken.id, //superTokenAddress + token.id, //tokenAddress + config.OPTIMISM_CONFIG.id, + isSafeEnv, + ); + if (approve) { + setStep(EModifySuperTokenSteps.DEPOSIT); + } else { + setStep(EModifySuperTokenSteps.APPROVE); + } + } catch (error) { + console.log('error', error); + setStep(EModifySuperTokenSteps.APPROVE); + } + }; + + const onDeposit = async () => { + setStep(EModifySuperTokenSteps.DEPOSITING); + try { + const _provider = getEthersProvider({ + chainId: config.OPTIMISM_CONFIG.id, + }); + + const signer = await getEthersSigner({ + chainId: config.OPTIMISM_CONFIG.id, + }); + + if (!_provider || !signer || !address || !superToken) + throw new Error('Provider or signer not found'); + + const sf = await Framework.create({ + chainId: config.OPTIMISM_CONFIG.id, + provider: _provider, + }); + + // EThx is not a Wrapper Super Token and should load separately + let superTokenAsset; + if (superToken.symbol === 'ETHx') { + superTokenAsset = await sf.loadNativeAssetSuperToken( + superToken.id, + ); + } else { + superTokenAsset = await sf.loadWrapperSuperToken(superToken.id); + } + const upgradeOperation = await superTokenAsset.upgrade({ + amount: amount.toString(), + }); + + const tx = await upgradeOperation.exec(signer); + const res = await tx.wait(); + if (!res.status) { + throw new Error('Deposit failed'); + } + setStep(EModifySuperTokenSteps.SUBMITTED); + } catch (error) { + setStep(EModifySuperTokenSteps.DEPOSIT); + showToastError(error); + console.log('error', error); + } + }; + + const onAction = () => { + if (step === EModifySuperTokenSteps.MODIFY) { + setStep(EModifySuperTokenSteps.APPROVE); + } else if (step === EModifySuperTokenSteps.APPROVE) { + onApprove(); + } else if (step === EModifySuperTokenSteps.DEPOSIT) { + onDeposit(); + } else if (step === EModifySuperTokenSteps.SUBMITTED) { + setShowModal(false); + } + }; + + return ( + + {step === EModifySuperTokenSteps.MODIFY ? ( + <> + + + + {formatMessage({ + id: 'label.top_up_stream_balance', + })} + + } + direction='right' + align='bottom' + > + PlaceHolder + + + + + + {token?.symbol && ( + + )} + {token?.symbol} + + + + + + + {formatMessage({ + id: 'label.available', + })} + : {balance?.formatted} + + !isRefetching && refetch()} + > + {isRefetching ? ( + + ) : ( + + )} + + + + + + {formatMessage({ + id: 'label.stream_balance', + })} + + + {SuperTokenBalance?.formatted}{' '} + {superToken?.symbol} + + + + + {formatMessage({ + id: 'label.balance_runs_out_in', + })}{' '} + + {formatMessage( + { + id: 'label.months', + }, + { + count: streamRunOutInMonth.toString(), + }, + )} + + + + {formatMessage({ id: 'label.funding' })}{' '} + {tokenStream.length}{' '} + {formatMessage( + { id: 'label.project' }, + { + count: tokenStream.length, + }, + )} + + + + + + + ) : ( + + + + + + )} +