import React, { ReactElement, useEffect, useState } from 'react'; import './index.scss'; import Stepper from '@mui/material/Stepper'; import Step from '@mui/material/Step'; import Box from '@mui/material/Box'; import StepLabel from '@mui/material/StepLabel'; import classNames from 'classnames'; import type { PresentationJSON } from 'tlsn-js/build/types'; import Button from '../Button'; import ConfettiExplosion, { ConfettiProps } from 'react-confetti-explosion'; const steps = [ 'Connect Extension', 'Install Plugin', 'Run Plugin', '🎉 Claim POAP 🎉', ]; export default function Steps(): ReactElement { const [extensionInstalled, setExtensionInstalled] = useState(false); const [pluginID, setPluginID] = useState(''); const [step, setStep] = useState(0); const [client, setClient] = useState(null); const [pluginData, setPluginData] = useState(null); const [loading, setLoading] = useState(false); const [pluginInstalled, setPluginInstalled] = useState(false); const [transcript, setTranscript] = useState(null); const [screenName, setScreenName] = useState(''); const [exploding, setExploding] = useState(false); useEffect(() => { const checkExtension = () => { //@ts-ignore if (typeof window.tlsn !== 'undefined') { setExtensionInstalled(true); setTimeout(async () => { // temporary fix until extension events added // @ts-ignore setClient(await window.tlsn.connect()); setStep(1); }, 200); } else { return; } }; window.onload = () => { checkExtension(); }; (async () => { const { default: init } = await import('tlsn-js'); await init(); })(); return () => { window.onload = null; }; }, []); useEffect(() => { if (transcript) { const match = transcript.recv.match(/"screen_name":"([^"]+)"/); const screenName = match ? match[1] : null; setScreenName(screenName); setExploding(true); } }, [transcript]); async function handleConnect() { try { //@ts-ignore setClient(await window.tlsn.connect()); setStep(1); } catch (error) { console.log(error); } } async function handleGetPlugins() { try { const plugins = await client.getPlugins('**', '**', { id: 'twitter-plugin', }); if (plugins.length > 0) { setPluginID(plugins[0].hash); setStep(2); } else { setPluginInstalled(true); } } catch (error) { console.log(error); } } async function handlePluginInstall() { try { const plugin = await client.installPlugin( 'https://github.com/tlsnotary/tlsn-extension/raw/main/src/assets/plugins/twitter_profile.wasm', { id: 'twitter-plugin' }, ); setPluginID(plugin); setStep(2); } catch (error) { console.log(error); } } async function handleRunPlugin() { try { setLoading(true); const pluginData = await client.runPlugin(pluginID); setLoading(false); setPluginData(pluginData); setStep(3); } catch (error) { setLoading(false); console.log(error); } } return (
{extensionInstalled ? ( <>
Connected{' '}
= 1, 'bg-red-500': step === 0, }, )} >
{steps.map((label) => ( {label} ))}
{step === 0 && ( )} {step === 1 && (
)} {step === 2 && (
Please keep the sidebar open during the notarization process
)} {step === 4 && ( <> )}
) : (
Install TLSN Extension

Please install the extension to proceed.

You will need to refresh your browser after installing the extension.

)}
); } function DisplayPluginData({ step, pluginData, transcript, setTranscript, setStep, }: { step: number; pluginData: any; transcript: any; setTranscript: any; setStep: any; }): ReactElement { const [tab, setTab] = useState<'sent' | 'recv'>('sent'); async function handleVerify() { try { const { Presentation, Transcript } = await import('tlsn-js'); const presentation = await new Presentation(pluginData.data); const proof = await presentation.verify(); const transcript = new Transcript({ sent: proof.transcript.sent, recv: proof.transcript.recv, }); const verifiedData = { sent: transcript.sent(), recv: transcript.recv(), }; setTranscript(verifiedData); setStep(4); } catch (error) { console.log(error); } } const formatDataPreview = (data: PresentationJSON) => { if (!data) return ''; return Object.entries(data) .map(([key, value]) => { if (typeof value === 'object' && value !== null) { return `${key}: ${JSON.stringify(value, null, 2)}`; } else if (key === 'data') { const maxLength = 160; const previewData = value.toString().substring(0, maxLength); const formattedData = previewData.match(/.{1,20}/g)?.join('\n'); return `${key}: ${formattedData}... ${value.length} more`; } else { return `${key}: ${value}`; } }) .join('\n'); }; return (
Attestation
            {formatDataPreview(pluginData)}
          
Presentation
              {transcript &&
                (tab === 'sent' ? transcript.sent : transcript.recv)}
            
); } function ClaimPoap({ screen_name, exploding, }: { screen_name: string; exploding: boolean; }): ReactElement { const [screenName, setScreenName] = useState(''); const [poapLink, setPoapLink] = useState(''); const [error, setError] = useState(null); useEffect(() => { const handleClaimPoap = async () => { try { if (!screen_name) return; const response = await fetch('/poap-claim', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ screenName: screen_name }), }); if (response.status === 200) { const data = await response.json(); setPoapLink(data.poapLink); } else { setError(await response.text()); } } catch (error) { console.log(error); } }; handleClaimPoap(); }, [screen_name]); const mediumProps: ConfettiProps = { force: 0.6, duration: 4000, particleCount: 150, width: 1500, colors: ['#F0FFF', '#F0F8FF', '#483D8B', '#E0FFF', '#778899'], }; return (
{poapLink !== '' && ( Claim POAP! )} {exploding && }
); }