-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: domain gift page #801
Changes from 5 commits
ba22ed4
83269b3
89ee166
9ea8ecd
c5a0c7c
10063fd
eb0c9b5
59ddb60
5d71464
c691787
23f9132
825ef55
070e7ed
60679a7
b81af6f
baf51ff
2061244
8177da2
6fd2460
e1d7dc0
2ea43bd
462cf13
ed5b0e1
e2a19d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React, { FunctionComponent } from "react"; | ||
import styles from "../../styles/discount.module.css"; | ||
import SearchBar from "../UI/searchBar"; | ||
import Timer from "../UI/timer"; | ||
|
||
type DiscountOfferScreenVariantProps = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change name to what FreeRegisterDiscount |
||
setSearchResult: (searchResult: SearchResult) => void; | ||
setScreen: (screen: number) => void; | ||
title: { desc: string; catch: string; descAfter?: string }; | ||
desc: string; | ||
image: string; | ||
expiry: number; | ||
}; | ||
|
||
const DiscountOfferScreenVariant: FunctionComponent< | ||
DiscountOfferScreenVariantProps | ||
> = ({ setSearchResult, setScreen, title, desc, image, expiry }) => { | ||
function onSearch(searchResult: SearchResult) { | ||
setSearchResult(searchResult); | ||
setScreen(2); | ||
} | ||
|
||
return ( | ||
<div className={styles.wrapperScreen}> | ||
<div className={styles.containerVariant}> | ||
<div className="max-w-4xl flex flex-col items-start justify-start gap-5 mx-5 mb-5"> | ||
<div className="flex flex-col lg:items-start items-center text-center sm:text-start gap-3"> | ||
<h1 className={styles.titleVariant}> | ||
{title.desc} <span className="text-primary">{title.catch}</span> | ||
{title.descAfter && " " + title.descAfter} | ||
</h1> | ||
<p className={styles.descriptionVariant}>{desc}</p> | ||
</div> | ||
<div className={styles.searchBarVariant}> | ||
<SearchBar onSearch={onSearch} showHistory={false} is5LettersOnly /> | ||
</div> | ||
</div> | ||
<div className={styles.illustrationContainerVariant}> | ||
<img src={image} className={styles.illustrationVariant} /> | ||
<Timer expiry={expiry} fixed /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default DiscountOfferScreenVariant; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,15 @@ import { | |
getDomainPriceAltcoin, | ||
getTokenQuote, | ||
} from "../../utils/altcoinService"; | ||
import { getFreeDomain } from "@/utils/campaignService"; | ||
|
||
export type GetCustomCalls = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need for a type. Use the |
||
newTokenId: number, | ||
encodedDomain: string, | ||
signature: string[], | ||
coupon: string, | ||
txMetadataHash: HexString | ||
) => Call[]; | ||
|
||
type RegisterDiscountProps = { | ||
domain: string; | ||
|
@@ -49,6 +58,10 @@ type RegisterDiscountProps = { | |
priceInEth: string; | ||
mailGroups: string[]; | ||
goBack: () => void; | ||
couponCode?: boolean; | ||
couponHelper?: string; | ||
banner?: string; | ||
getCustomCalls?: GetCustomCalls; | ||
}; | ||
|
||
const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | ||
|
@@ -59,6 +72,10 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
priceInEth, | ||
mailGroups, | ||
goBack, | ||
couponCode, | ||
couponHelper, | ||
banner = "/visuals/register.webp", | ||
getCustomCalls, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of creating a you wouldn't need to pass the calls in props ... I think it would be clearer tbh |
||
}) => { | ||
const [targetAddress, setTargetAddress] = useState<string>(""); | ||
const [email, setEmail] = useState<string>(""); | ||
|
@@ -89,6 +106,11 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
const [domainsMinting, setDomainsMinting] = useState<Map<string, boolean>>( | ||
new Map() | ||
); | ||
const [coupon, setCoupon] = useState<string>(""); | ||
const [lastSuccessCoupon, setLastSuccessCoupon] = useState<string>(""); | ||
const [couponError, setCouponError] = useState<string>(""); | ||
const [signature, setSignature] = useState<string[]>(["", ""]); | ||
const [loadingCoupon, setLoadingCoupon] = useState<boolean>(false); | ||
const { addTransaction } = useNotificationManager(); | ||
const needsAllowance = useAllowanceCheck(displayedCurrency, address); | ||
const tokenBalances = useBalances(address); // fetch the user balances for all whitelisted tokens | ||
|
@@ -192,6 +214,17 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
const addressesMatch = | ||
hexToDecimal(address) === hexToDecimal(targetAddress); | ||
|
||
if (getCustomCalls) { | ||
const customCalls = getCustomCalls( | ||
newTokenId, | ||
encodedDomain, | ||
signature, | ||
coupon, | ||
txMetadataHash | ||
); | ||
return setCallData(customCalls); | ||
} | ||
|
||
// Common calls | ||
const calls = [ | ||
registrationCalls.approve(price, ERC20Contract[displayedCurrency]), | ||
|
@@ -282,6 +315,9 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
discountId, | ||
quoteData, | ||
displayedCurrency, | ||
coupon, | ||
signature, | ||
getCustomCalls, | ||
]); | ||
|
||
useEffect(() => { | ||
|
@@ -322,6 +358,34 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
setEmailError(isValidEmail(value) ? false : true); | ||
} | ||
|
||
function changeCoupon(value: string): void { | ||
setCoupon(value); | ||
setLoadingCoupon(true); | ||
} | ||
|
||
useEffect(() => { | ||
if (!coupon) { | ||
setCouponError("Please enter a coupon code"); | ||
setLoadingCoupon(false); | ||
return; | ||
} | ||
if (coupon === lastSuccessCoupon) { | ||
setCouponError(""); | ||
setLoadingCoupon(false); | ||
return; | ||
} | ||
if (!address) return; | ||
getFreeDomain(address, `${domain}.stark`, coupon).then((res) => { | ||
if (res.error) setCouponError(res.error); | ||
else { | ||
setSignature([res.r, res.s]); | ||
setLastSuccessCoupon(coupon); | ||
setCouponError(""); | ||
} | ||
setLoadingCoupon(false); | ||
}); | ||
Marchand-Nicolas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, [coupon, domain, address, lastSuccessCoupon]); | ||
|
||
useEffect(() => { | ||
if (isSwissResident) { | ||
setSalesTaxRate(swissVatRate); | ||
|
@@ -347,6 +411,8 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
setDisplayedCurrency(type); | ||
}; | ||
|
||
const isFree = priceInEth === "0"; | ||
|
||
return ( | ||
<div className={styles.container}> | ||
<div className={styles.card}> | ||
|
@@ -370,6 +436,17 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
isSwissResident={isSwissResident} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ![]() In the process of trying to make things as simple as possible, I would like to delete both of these Considering this it might be better to rewrite a component There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
onSwissResidentChange={() => setIsSwissResident(!isSwissResident)} | ||
/> | ||
{couponCode ? ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes so this is the kind of stuff we want to avoid in a component in general to make it as general as possible |
||
<TextField | ||
helperText={couponHelper} | ||
label="Coupon code" | ||
value={coupon} | ||
onChange={(e) => changeCoupon(e.target.value)} | ||
color="secondary" | ||
error={Boolean(couponError)} | ||
errorMessage={couponError} | ||
/> | ||
) : null} | ||
</div> | ||
</div> | ||
<div className={styles.summary}> | ||
|
@@ -384,6 +461,7 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
onCurrencySwitch={onCurrencySwitch} | ||
customMessage={customMessage} | ||
loadingPrice={loadingPrice} | ||
isFree={isFree} | ||
/> | ||
<Divider className="w-full" /> | ||
<RegisterCheckboxes | ||
|
@@ -408,7 +486,9 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
!targetAddress || | ||
invalidBalance || | ||
!termsBox || | ||
emailError | ||
emailError || | ||
Boolean(couponError) || | ||
loadingCoupon | ||
} | ||
> | ||
{!termsBox | ||
|
@@ -417,14 +497,16 @@ const RegisterDiscount: FunctionComponent<RegisterDiscountProps> = ({ | |
? `You don't have enough ${displayedCurrency}` | ||
: emailError | ||
? "Enter a valid Email" | ||
: couponError | ||
? "Enter a valid Coupon" | ||
: "Register my domain"} | ||
</Button> | ||
) : ( | ||
<ConnectButton /> | ||
)} | ||
</div> | ||
</div> | ||
<img className={styles.image} src="/visuals/register.webp" /> | ||
<img className={styles.image} src={banner} /> | ||
<TxConfirmationModal | ||
txHash={registerData?.transaction_hash} | ||
isTxModalOpen={isTxModalOpen} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import React, { useEffect, useState } from "react"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
import type { NextPage } from "next"; | ||
import homeStyles from "../styles/Home.module.css"; | ||
import DiscountEndScreen from "../components/discount/discountEndScreen"; | ||
import DiscountCheckoutScreen from "../components/discount/discountCheckoutScreen"; | ||
|
||
// Create a new discount in utils to create a new discount campaign | ||
import { domainGift } from "../utils/discounts/domainGift"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace domain gift by freeRegistration everywhere. It's a better naming cause for the code it gives more information about you're doing. |
||
import DiscountOfferScreenVariant from "@/components/discount/discountOfferScreenVariant"; | ||
import { GetCustomCalls } from "@/components/discount/registerDiscount"; | ||
|
||
const DomainGift: NextPage = () => { | ||
const [searchResult, setSearchResult] = useState<SearchResult | undefined>(); | ||
const [screen, setScreen] = useState<number>(1); | ||
|
||
useEffect(() => { | ||
const currentDate = new Date(); | ||
const timestamp = currentDate.getTime(); | ||
|
||
if (timestamp >= domainGift.expiry) setScreen(0); | ||
}, []); | ||
|
||
function goBack() { | ||
setScreen(screen - 1); | ||
} | ||
|
||
const getCustomCalls: GetCustomCalls = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add to |
||
newTokenId, | ||
encodedDomain, | ||
signature, | ||
coupon, | ||
txMetadataHash | ||
) => { | ||
return [ | ||
{ | ||
contractAddress: process.env.NEXT_PUBLIC_DOMAIN_GIFT_CONTRACT as string, | ||
entrypoint: "get_free_domain", | ||
calldata: [ | ||
newTokenId, | ||
encodedDomain, | ||
signature, | ||
coupon, | ||
txMetadataHash, | ||
].flat(), | ||
}, | ||
]; | ||
}; | ||
|
||
return ( | ||
<div className={homeStyles.screen}> | ||
{screen === 0 ? ( | ||
<DiscountEndScreen | ||
title={`${domainGift.name} discount has ended`} | ||
image={domainGift.image} | ||
/> | ||
) : null} | ||
{screen === 1 ? ( | ||
<DiscountOfferScreenVariant | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No description provided. |
||
title={domainGift.offer.title} | ||
desc={domainGift.offer.desc} | ||
image={domainGift.offer.image ?? domainGift.image} | ||
setSearchResult={setSearchResult} | ||
setScreen={setScreen} | ||
expiry={domainGift.expiry} | ||
/> | ||
) : null} | ||
{screen === 2 ? ( | ||
<DiscountCheckoutScreen | ||
domain={searchResult?.name ?? ""} | ||
duration={domainGift.offer.duration} | ||
discountId={domainGift.offer.discountId} | ||
customMessage={domainGift.offer.customMessage} | ||
price={domainGift.offer.price} | ||
goBack={goBack} | ||
mailGroupId={domainGift.discountMailGroupId} | ||
couponCode={domainGift.offer.couponCode} | ||
couponHelper={domainGift.offer.couponHelper} | ||
banner={domainGift.image} | ||
getCustomCalls={getCustomCalls} | ||
/> | ||
) : null} | ||
</div> | ||
); | ||
}; | ||
|
||
export default DomainGift; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this component really useful ? It seems like a wrapper for
RegisterDiscount
It would delete a lot of useless code to directly use
RegisterDiscount
where needed and add<div className={styles.container}>
in itYou have more context than me as I don't remember so well this part of the code. Wdyt ?