Skip to content
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

Verify Email Url #731

Merged
merged 4 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ NEXT_PUBLIC_FB_VAPID_KEY=<NEXT_PUBLIC_FB_VAPID_KEY>
NEXT_PUBLIC_FB_MEASUREMENT_ID=<NEXT_PUBLIC_FB_MEASUREMENT_ID>
NEXT_PUBLIC_MICROCREDIT_MANAGER_ADMIN=<NEXT_PUBLIC_MICROCREDIT_MANAGER_ADMIN>
NEXT_PUBLIC_CLIENT_ID=<NEXT_PUBLIC_CLIENT_ID>
NEXT_PUBLIC_VERIFY_EMAIL_URL=<NEXT_PUBLIC_VERIFY_EMAIL_URL>
7 changes: 6 additions & 1 deletion config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ const config = {
/*
* ClientId for L&E
*/
clientId: process.env.NEXT_PUBLIC_CLIENT_ID
clientId: process.env.NEXT_PUBLIC_CLIENT_ID,

/*
* Email Verification URL
*/
verifyEmailUrl: process.env.NEXT_PUBLIC_VERIFY_EMAIL_URL
};

export default config;
10 changes: 10 additions & 0 deletions src/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ export const userApi = emptySplitApi.injectEndpoints({
transformResponse: (response: { data: RecoverUser }) =>
response.data
}),
// Send Email
sendVerifyEmail: builder.mutation<any, any>({
query: (body) => ({
body,
method: 'POST',
url: '/users/request-verify'
}),
transformResponse: (response: { data: any }) => response.data
}),
// Mark notifications as read
updateNotifications: builder.mutation<Notification[], { body: any }>({
query: ({ body }) => ({
Expand Down Expand Up @@ -193,5 +202,6 @@ export const {
useUpdateNotificationsMutation,
useUpdateUserMutation,
useGetPreSignedMutation,
useSendVerifyEmailMutation,
useVerifyEmailMutation
} = userApi;
81 changes: 44 additions & 37 deletions src/views/LearnAndEarn/Metrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useState } from 'react';
import Message from '../../libs/Prismic/components/Message';
import RichText from '../../libs/Prismic/components/RichText';
import String from '../../libs/Prismic/components/String';
import ValidateEmail from './ValidateEmail';
import config from '../../../config';
import processTransactionError from '../../utils/processTransactionError';
import styled from 'styled-components';
Expand Down Expand Up @@ -134,47 +135,53 @@ const Metrics = (props: any) => {
</Display>
</ProgressCard>
))}
<Card
flex
style={{ alignItems: 'center', justifyContent: 'center' }}
h="100%"
>
<Box
{auth?.user?.emailValidated ? (
<Card
flex
fDirection={'column'}
style={{ alignItems: 'center' }}
style={{ alignItems: 'center', justifyContent: 'center' }}
h="100%"
>
<RichText
center
g500
medium
small
mb="1rem"
content={
hasRewards ? props.copy.success : props.copy.failed
}
/>
<RewardsButton
onClick={claimRewards}
{...disabled}
disabled={!hasRewards}
isLoading={isLoading}
<Box
flex
fDirection={'column'}
style={{ alignItems: 'center' }}
>
<String id="claimRewards" />
</RewardsButton>
<Text small semibold style={{ marginTop: '.5rem' }}>
<a
href={`mailto:[email protected]?subject=Learn%20and%20Earn%20-%20Opera&body=Please%20Describe%20Your%20Problem:%0A%0A%0A------------------------------%0A%0AYour%20Wallet%20Address:%0A${auth?.user?.address.toString()}%0A%0AWe%20collected%20your%20wallet%20address%20to%20analyze%20and%20resolve%20reported%20bugs.%20Without%20this%20information%20it%20may%20be%20difficult%20to%20provide%20proper%20help.%20Your%20funds%20remain%20secure.`}
style={{
color: '#5A6FEF',
textDecoration: 'none'
}}
<RichText
center
g500
medium
small
mb="1rem"
content={
hasRewards
? props.copy.success
: props.copy.failed
}
/>
<RewardsButton
onClick={claimRewards}
{...disabled}
disabled={!hasRewards}
isLoading={isLoading}
>
{view?.data['needHelp']}
</a>
</Text>
</Box>
</Card>
<String id="claimRewards" />
</RewardsButton>
<Text small semibold style={{ marginTop: '.5rem' }}>
<a
href={`mailto:[email protected]?subject=Learn%20and%20Earn%20-%20Webapp&body=Please%20Describe%20Your%20Problem:%0A%0A%0A------------------------------%0A%0AYour%20Wallet%20Address:%0A${auth?.user?.address.toString()}%0A%0AWe%20collected%20your%20wallet%20address%20to%20analyze%20and%20resolve%20reported%20bugs.%20Without%20this%20information%20it%20may%20be%20difficult%20to%20provide%20proper%20help.%20Your%20funds%20remain%20secure.`}
style={{
color: '#5A6FEF',
textDecoration: 'none'
}}
>
{view?.data['needHelp']}
</a>
</Text>
</Box>
</Card>
) : (
<ValidateEmail />
)}
</CardsGrid>
);
};
Expand Down
253 changes: 253 additions & 0 deletions src/views/LearnAndEarn/ValidateEmail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// @ts-nocheck
import {
Box,
Button,
Card,
CircledIcon,
Icon,
Input,
Text,
TextLink,
colors,
toast
} from '@impact-market/ui';
import { selectCurrentUser } from 'src/state/slices/auth';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useSendVerifyEmailMutation } from 'src/api/user';
import RichText from '../../libs/Prismic/components/RichText';
import config from 'config';
import processTransactionError from '../../utils/processTransactionError';
import router from 'next/router';
import styled from 'styled-components';

const ConsentWrapper = styled(Box)`
display: flex;
flex-direction: row;
margin-top: 1rem;
gap: 0.5rem;
align-items: center;
`;

const CheckBox = styled(Box)`
background-color: ${colors.p100};
border-radius: 5px;
height: 20px;
width: 20px;
`;

const IconStyled = styled(Icon)`
color: ${colors.p500};
height: 100%;
width: 30px;
margin: 0 auto;
`;

const ButtonStyled = styled(Button)`
margin-top: 1rem;
border: none;
`;

const ValidateEmail = () => {
const auth = useSelector(selectCurrentUser);
const [sendVerifyEmail] = useSendVerifyEmailMutation();
const [openForm, setOpenForm] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [consent, setConsent] = useState(false);
const [email, setEmail] = useState('');
const [isEmailValid, setIsEmailValid] = useState(true);
const [success, setSuccess] = useState(false);

useEffect(() => {
if (auth?.user?.email) {
setEmail(auth?.user?.email);
}
}, [auth]);

const verifyEmail = async () => {
if (!validateEmail(email)) {
setIsEmailValid(false);

return;
}

try {
await sendVerifyEmail({
email,
url: config.verifyEmailUrl
}).unwrap();

setIsLoading(false);
setOpenForm(false);
setSuccess(true);
} catch (error) {
toast.error(`An error has occurred. Please try again later.`);
processTransactionError(error, 'verify_email');
console.log(error);
}
};

const validateEmail = (email: string) => {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

return re.test(email);
};

const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEmail(e.target.value);
setIsEmailValid(true);
};

return (
<Card
className="claim-rewards"
style={{ boxSizing: 'border-box', flex: '1' }}
>
<Box
style={{
display: 'flex',
flexDirection: 'column'
}}
>
{success ? (
<CircledIcon
icon="check"
success
style={{ margin: '0 auto' }}
/>
) : (
<IconStyled icon="mail" />
)}
<RichText
style={{
color: `${colors.g700}`,
marginTop: '0.5rem',
textAlign: 'center'
}}
content={
!success
? 'Confirm email to claim rewards'
: 'Check your inbox! '
}
semibold
large
/>

{openForm ? (
<>
<Box style={{ marginTop: '1rem', width: '100%' }}>
<Input
id="email"
placeholder="Add email"
onChange={handleEmailChange}
value={email}
style={{
color: '#101828',
paddingLeft: '0.5rem'
}}
icon="mail"
/>
{!isEmailValid && (
<Text
extrasmall
style={{
color: colors.e600,
paddingTop: '0.5rem'
}}
>
Please enter a valid address
</Text>
)}
</Box>
<ConsentWrapper>
<Box mr={0.6}>
<CheckBox
onClick={() => setConsent(!consent)}
padding={0.3}
flex
>
{consent && (
<Icon
icon="tick"
h="100%"
w="100%"
style={{ color: colors.p500 }}
/>
)}
</CheckBox>
</Box>
<label style={{ textAlign: 'left' }}>
<Text small style={{ color: colors.g700 }}>
I agree to the Privacy Policy and to receive
updates from impactMarket.
</Text>
</label>
</ConsentWrapper>
<ButtonStyled
isLoading={isLoading}
onClick={verifyEmail}
disabled={!consent || !email}
success
>
Confirm email
</ButtonStyled>
</>
) : (
<>
{!success && (
<ButtonStyled
onClick={() => setOpenForm(true)}
isLoading={isLoading}
success
>
Continue
</ButtonStyled>
)}

{success && (
<>
<Text
style={{
color: colors.g700,
textAlign: 'center'
}}
>
We've sent you an email to {email}.
</Text>
<TextLink
onClick={() => {
setSuccess(false);
setOpenForm(true);
}}
style={{
display: 'flex',
justifyContent: 'center',
marginTop: '1rem'
}}
>
<Text style={{ color: colors.s400 }}>
Change email
</Text>
</TextLink>
</>
)}
</>
)}
<TextLink
onClick={() => {
router.reload();
}}
style={{
display: 'flex',
justifyContent: 'center',
marginTop: '1rem'
}}
>
<Text extrasmall>Refresh page</Text>
</TextLink>
</Box>
</Card>
);
};

export default ValidateEmail;
Loading