Skip to content

Commit

Permalink
add discourse (#75)
Browse files Browse the repository at this point in the history
* add discourse

* fix

* fix

* update ts config

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix
  • Loading branch information
mehdi-torabiv authored Jan 20, 2025
1 parent e71e426 commit 39e3596
Show file tree
Hide file tree
Showing 14 changed files with 12,644 additions and 20,940 deletions.
33,001 changes: 12,070 additions & 20,931 deletions package-lock.json

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions src/components/pages/attestations/DiscourseStepFour.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { useState } from 'react';
import {
Box,
Button,
CircularProgress,
Stack,
Typography,
} from '@mui/material';
import { FaLink } from 'react-icons/fa';
import { useNavigate } from 'react-router-dom';
import { Address } from 'viem';
import { useAccount } from 'wagmi';

import { AttestPayload } from '../../../interfaces';
import EASService from '../../../services/eas.service';
import useSnackbarStore from '../../../store/useSnackbarStore';
import { contracts } from '../../../utils/contracts/eas/contracts';
import { useSigner } from '../../../utils/eas-wagmi-utils';

interface DiscourseStepFourProps {
attestedSignutare: AttestPayload | null;
}

const DiscourseStepFour: React.FC<DiscourseStepFourProps> = ({
attestedSignutare,
}) => {
const { showSnackbar } = useSnackbarStore();
const navigate = useNavigate();
const signer = useSigner();
const { chainId } = useAccount();
const [isLoading, setIsLoading] = useState<boolean>(false);

const easContractAddress = contracts.find(
(contract) => contract.chainId === chainId
)?.easContractAddress;

const easService = signer
? new EASService(easContractAddress as Address, signer)
: null;

const handleAttestByDelegation = async () => {
if (!easService) {
throw new Error('EAS service not initialized');
}
if (!attestedSignutare) throw new Error('No attested signature provided');

setIsLoading(true);
try {
await easService.attestByDelegation(attestedSignutare);
showSnackbar('Attestation successfully completed.', {
severity: 'success',
});
navigate('/identifiers');
} catch (error) {
console.error('Error attesting identifier:', error);
showSnackbar('Failed to complete the attestation. Please try again.', {
severity: 'error',
});
} finally {
setIsLoading(false);
}
};

return (
<Stack
spacing={3}
sx={{
textAlign: 'center',
py: 12,
px: 2,
}}
>
<Typography variant="h5" fontWeight="bold">
Finalize Delegated Attestation
</Typography>
<Typography>
To complete the process, you will be asked to sign a message with your
wallet, confirming ownership of the provided address.
</Typography>
<Box>
<Button
variant="contained"
startIcon={
isLoading ? (
<CircularProgress color="inherit" size={20} />
) : (
<FaLink />
)
}
sx={{ mt: 2, px: 4 }}
onClick={handleAttestByDelegation}
disabled={isLoading}
>
{isLoading ? 'Processing...' : 'Sign Delegated Attestation'}
</Button>
</Box>
<Typography variant="caption">
You need to pay some <b>gas</b> to complete the process.
</Typography>
</Stack>
);
};

export default DiscourseStepFour;
83 changes: 83 additions & 0 deletions src/components/pages/attestations/DiscourseStepOne.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Box, Button, Stack, Typography } from '@mui/material';
import { jwtDecode } from 'jwt-decode';
import { FaDiscourse } from 'react-icons/fa';

import { useGenerateDiscourseVerificationTokenMutation } from '../../../services/api/eas/query';
import useSnackbarStore from '../../../store/useSnackbarStore';

interface DiscourseStepOneProps {
handleNextStep: () => void;
}

const DiscourseStepOne: React.FC<DiscourseStepOneProps> = ({
handleNextStep,
}) => {
const { showSnackbar } = useSnackbarStore();
const { mutate: mutateGenerateDiscourseVerificationToken, isPending } =
useGenerateDiscourseVerificationTokenMutation();

const handleGenerateDiscourseVerificationToken = async () => {
const siweJwt = localStorage.getItem('OCI_TOKEN') as string;

mutateGenerateDiscourseVerificationToken(
{
siweJwt,
},
{
onSuccess: (response) => {
const { data } = response;

localStorage.setItem(
'DISCOURSE_VERIFICATION_TOKEN',
data.verificationJwt
);

const { code } = jwtDecode(data.verificationJwt) as { code: string };
localStorage.setItem('DISCOURSE_VERIFICATION_CODE', code);

handleNextStep();
},
onError: (error) => {
console.error('Failed to generate token:', error);
showSnackbar(
'Failed to generate verification token. Please try again.',
{
severity: 'error',
}
);
},
}
);
};

return (
<Stack
spacing={2}
sx={{
textAlign: 'center',
py: 12,
}}
>
<Typography variant="h5" fontWeight="bold">
Let&rsquo;s get started!
</Typography>
<Typography variant="body2">
To attest your Discourse account, you need to generate a token.
</Typography>
<Box sx={{ display: 'block' }}>
<Button
variant="contained"
startIcon={<FaDiscourse />}
disabled={isPending}
onClick={handleGenerateDiscourseVerificationToken}
aria-busy={isPending}
aria-live="polite"
>
{isPending ? 'Generating token...' : 'Generate token'}
</Button>
</Box>
</Stack>
);
};

export default DiscourseStepOne;
96 changes: 96 additions & 0 deletions src/components/pages/attestations/DiscourseStepThree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React from 'react';
import {
Box,
Button,
CircularProgress,
Stack,
Typography,
} from '@mui/material';
import { FaLink } from 'react-icons/fa6';
import { useAccount } from 'wagmi';

import { Provider } from '../../../enums';
import { AttestPayload } from '../../../interfaces';
import { useLinkIdentifierMutation } from '../../../services/api/eas/query';
import { capitalize } from '../../../utils/helper';

interface DiscourseStepThreeProps {
provider: Provider | undefined;
handlePrepareAttestation: (payload: AttestPayload) => void;
}

const DiscourseStepThree: React.FC<DiscourseStepThreeProps> = ({
provider,
handlePrepareAttestation,
}) => {
const { chainId } = useAccount();
const { mutate: mutateIdentifier, isPending } = useLinkIdentifierMutation(
chainId as number
);

const handleGenerateSignedDelegation = async () => {
const siweJwt = localStorage.getItem('OCI_TOKEN');
if (!siweJwt || !provider) return;

const anyJwt = localStorage.getItem('DISCOURSE_JWT') as string;

mutateIdentifier(
{
siweJwt,
anyJwt,
},
{
onSuccess: (response) => {
const { data } = response;
handlePrepareAttestation(data);
},
onError: (error) => {
console.error(error);
},
}
);
};

if (!provider) {
return null;
}

return (
<Stack
spacing={3}
sx={{
textAlign: 'center',
py: 12,
px: 2,
}}
>
<Typography variant="h5" fontWeight="bold">
Connect Your {capitalize(provider)} Account to Your Wallet
</Typography>
<Typography>
To proceed, please verify your account by linking it to your wallet
address. This step ensures your {capitalize(provider)} account is
securely associated with your wallet.
</Typography>
<Box>
<Button
variant="contained"
startIcon={
isPending ? (
<CircularProgress color="inherit" size={20} />
) : (
<FaLink />
)
}
sx={{ mt: 2, px: 4 }}
onClick={handleGenerateSignedDelegation}
disabled={isPending}
>
{isPending ? 'Processing...' : 'Get Signed Delegated Attestation'}
</Button>
</Box>
</Stack>
);
};

export default DiscourseStepThree;
Loading

0 comments on commit 39e3596

Please sign in to comment.