import React, { useState, useEffect } from 'react'; import { getDigiByteAddressType } from './utils'; // Import the address type helper // Define the possible UI states type UiState = 'initial' | 'waiting' | 'success' | 'failed'; // Define the structure for result data (success or failure) interface ResultData { address?: string; // Present on success error?: string; // Present on failure addressType?: string; // Added later } function App() { const [uiState, setUiState] = useState('initial'); const [sessionId, setSessionId] = useState(null); const [qrCodeDataUrl, setQrCodeDataUrl] = useState(null); const [resultData, setResultData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // For general fetch errors // Polling interval for status check (in milliseconds) const POLLING_INTERVAL = 2000; // Check every 2 seconds // Effect for polling the status endpoint when in 'waiting' state useEffect(() => { // Only poll if we are in the waiting state and have a session ID if (uiState !== 'waiting' || !sessionId) { return; // Exit if not applicable } console.log(`Starting status polling for session: ${sessionId}`); let intervalId: any = null; // Use 'any' to avoid browser/node type conflict for setInterval return type const checkStatus = async () => { if (!sessionId) return; // Should not happen here, but type guard console.log(`Checking status for session: ${sessionId}`); try { const response = await fetch(`/api/digiid/status/${sessionId}`); if (!response.ok) { // Handle specific errors like 404 (session not found/expired) if (response.status === 404) { console.warn(`Session ${sessionId} not found or expired during polling.`); setError('Session expired or could not be found.'); setUiState('failed'); // Transition to failed state setResultData({ error: 'Session expired or could not be found.' }); } else { const errorData = await response.json().catch(() => ({ message: 'Error fetching status' })); throw new Error(errorData.message || `Server responded with ${response.status}`); } if (intervalId) clearInterval(intervalId); // Stop polling on error return; } const data: { status: UiState, address?: string, error?: string } = await response.json(); console.log('Received status data:', data); // If status changed from pending, update UI and stop polling if (data.status === 'success') { setResultData({ address: data.address }); setUiState('success'); if (intervalId) clearInterval(intervalId); } else if (data.status === 'failed') { setResultData({ error: data.error || 'Authentication failed.' }); setUiState('failed'); if (intervalId) clearInterval(intervalId); } // If status is still 'pending', the interval will continue } catch (err) { console.error('Error polling status:', err); const message = err instanceof Error ? err.message : 'An unknown error occurred during status check'; setError(`Status polling failed: ${message}`); // Decide if we should stop polling or transition state on generic fetch error // For now, let's stop polling and show error, moving to failed state setResultData({ error: `Status polling failed: ${message}` }); setUiState('failed'); if (intervalId) clearInterval(intervalId); } }; // Start the interval intervalId = setInterval(checkStatus, POLLING_INTERVAL); // Cleanup function to clear the interval when the component unmounts // or when the dependencies (uiState, sessionId) change return () => { if (intervalId) { console.log(`Stopping status polling for session: ${sessionId}`); clearInterval(intervalId); } }; }, [uiState, sessionId]); // Dependencies for the effect const handleStart = async () => { setIsLoading(true); setError(null); setQrCodeDataUrl(null); // Clear previous QR code if any setSessionId(null); setResultData(null); console.log('Requesting new Digi-ID session...'); try { const response = await fetch('/api/digiid/start'); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: 'Failed to start session. Server responded with status: ' + response.status })); throw new Error(errorData.message || 'Failed to start session'); } const data = await response.json(); console.log('Received session data:', data); setSessionId(data.sessionId); setQrCodeDataUrl(data.qrCodeDataUrl); setUiState('waiting'); // Move to waiting state } catch (err) { console.error('Error starting Digi-ID session:', err); const message = err instanceof Error ? err.message : 'An unknown error occurred'; setError(`Failed to initiate Digi-ID: ${message}`); setUiState('initial'); // Stay in initial state on error } finally { setIsLoading(false); } }; const handleReset = () => { // TODO: Implement reset logic console.log('Resetting flow...'); setUiState('initial'); setSessionId(null); setQrCodeDataUrl(null); setResultData(null); setError(null); setIsLoading(false); }; return (

Digi-ID TypeScript Integration Demo

{/* --- Views based on uiState --- */} {uiState === 'initial' && (
)} {uiState !== 'initial' && (
{uiState === 'waiting' && qrCodeDataUrl && (

Scan the QR Code

Scan the QR code below using your Digi-ID compatible mobile wallet.

Digi-ID QR Code

Waiting for authentication...

)} {uiState === 'success' && resultData?.address && (

Authentication Successful!

Verified Address:

{resultData.address}

Address Format: {getDigiByteAddressType(resultData.address)}

)} {uiState === 'failed' && (

Authentication Failed

Reason: {resultData?.error || error || 'An unknown error occurred.'}

)}
)} {/* --- Description section (Always visible below the view container) --- */}

This application demonstrates integrating Digi-ID authentication using the digiid-ts library.

Upon successful verification, the system can identify the following DigiByte address formats:

  • Legacy Addresses (P2PKH) - starting with 'D'
  • Pay-to-Script-Hash Addresses (P2SH) - commonly starting with 'S'
  • Segregated Witness (SegWit) Addresses - starting with 'dgb1'
); } export default App;