diff options
author | Pawel Zelawski <pawel.zelawski@outlook.com> | 2025-04-10 14:35:56 +0200 |
---|---|---|
committer | Pawel Zelawski <pawel.zelawski@outlook.com> | 2025-04-10 14:35:56 +0200 |
commit | d858bd4e0fc7aaba15509a17b35138e843667bc1 (patch) | |
tree | b8251d1579bebe7128efe5b93ed734f9a75b234b /src/client | |
parent | e29a1b9a652fe254d13afef7f5c7a3d7a64b68cc (diff) |
refactor: improve layout and spacing across all views
- Remove extra space at the top of the page by adjusting body and root styles
- Add consistent 20px top padding to all views through app-container
- Simplify layout structure by removing unnecessary flex containers
- Adjust spacing between elements for better visual hierarchy:
- Reduce gaps between elements from 1.5rem to 1rem
- Reduce bottom margin of h1 from 1.5rem to 1rem
- Remove negative margins and extra padding
- Maintain consistent box dimensions (390px width, 560px height) for all views
- Ensure proper spacing for QR code and other content within views
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/App.tsx | 101 | ||||
-rw-r--r-- | src/client/index.css | 301 | ||||
-rw-r--r-- | src/client/utils.ts | 33 |
3 files changed, 344 insertions, 91 deletions
diff --git a/src/client/App.tsx b/src/client/App.tsx index 00280cd..b3f0308 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -99,7 +99,7 @@ function App() { setQrCodeDataUrl(null); // Clear previous QR code if any setSessionId(null); setResultData(null); - console.log('Requesting new DigiID session...'); + console.log('Requesting new Digi-ID session...'); try { const response = await fetch('/api/digiid/start'); @@ -113,9 +113,9 @@ function App() { setQrCodeDataUrl(data.qrCodeDataUrl); setUiState('waiting'); // Move to waiting state } catch (err) { - console.error('Error starting DigiID session:', err); + console.error('Error starting Digi-ID session:', err); const message = err instanceof Error ? err.message : 'An unknown error occurred'; - setError(`Failed to initiate DigiID: ${message}`); + setError(`Failed to initiate Digi-ID: ${message}`); setUiState('initial'); // Stay in initial state on error } finally { setIsLoading(false); @@ -135,57 +135,70 @@ function App() { return ( <div className="app-container"> - <h1>DigiID-TS Demo</h1> + <h1>Digi-ID TypeScript Integration Demo</h1> - {/* --- Initial State --- */} + {/* --- Views based on uiState --- */} {uiState === 'initial' && ( <div className="initial-view"> - <h2>Welcome</h2> - <p>Click the button below to generate a DigiID login QR code.</p> - {/* TODO: Add Icon here */} - {/* <img src="/assets/YOUR_ICON_FILENAME.png" alt="DigiID Icon" width="100" /> */} - <button onClick={handleStart} disabled={isLoading}> - {isLoading ? 'Generating QR...' : 'Start DigiID Login'} + <button onClick={handleStart} disabled={isLoading} className="signin-button"> + <img src="/assets/digiid-logo.png" alt="Digi-ID Logo" width="24" height="24" /> + <span>{isLoading ? 'Generating QR...' : 'Sign in with Digi-ID'}</span> </button> </div> )} - {/* --- Waiting State --- */} - {uiState === 'waiting' && qrCodeDataUrl && ( - <div className="waiting-view"> - <h2>Scan the QR Code</h2> - <p>Scan the QR code below using your DigiID compatible mobile wallet.</p> - <img src={qrCodeDataUrl} alt="DigiID QR Code" width="250" /> - <p>Waiting for authentication...</p> - {/* Optional: Add a cancel button here */} - <button onClick={handleReset}>Cancel</button> + {uiState !== 'initial' && ( + <div className="view-container"> + {uiState === 'waiting' && qrCodeDataUrl && ( + <div className="waiting-view view-box"> + <h2>Scan the QR Code</h2> + <p>Scan the QR code below using your Digi-ID compatible mobile wallet.</p> + <img src={qrCodeDataUrl} alt="Digi-ID QR Code" width="250" /> + <p>Waiting for authentication...</p> + <button onClick={handleReset}>Cancel</button> + </div> + )} + + {uiState === 'success' && resultData?.address && ( + <div className="success-view view-box"> + <svg xmlns="http://www.w3.org/2000/svg" className="checkmark-icon" viewBox="0 0 52 52"> + <circle className="checkmark__circle" cx="26" cy="26" r="25" fill="none"/> + <path className="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/> + </svg> + <h2>Authentication Successful!</h2> + <p>Verified Address:</p> + <p className="address">{resultData.address}</p> + <p>Address Format: {getDigiByteAddressType(resultData.address)}</p> + <button onClick={handleReset}>Start Over</button> + </div> + )} + + {uiState === 'failed' && ( + <div className="failed-view view-box"> + <h2>Authentication Failed</h2> + <p className="error-message"> + Reason: {resultData?.error || error || 'An unknown error occurred.'} + </p> + <button onClick={handleReset}>Try Again</button> + </div> + )} </div> )} - {/* --- Success State --- */} - {uiState === 'success' && resultData?.address && ( - <div className="success-view"> - <h2>Authentication Successful!</h2> - <p>Verified Address:</p> - <p className="address">{resultData.address}</p> - <p>Address Type: {getDigiByteAddressType(resultData.address)}</p> - <button onClick={handleReset}>Start Over</button> - </div> - )} - - {/* --- Failed State --- */} - {uiState === 'failed' && ( - <div className="failed-view"> - <h2>Authentication Failed</h2> - <p className="error-message"> - Reason: {resultData?.error || error || 'An unknown error occurred.'} - </p> - <button onClick={handleReset}>Try Again</button> - </div> - )} - - {/* General error display (e.g., for initial start error) */} - {error && uiState === 'initial' && <p className="error-message">Error: {error}</p>} + {/* --- Description section (Always visible below the view container) --- */} + <div className="description"> + <p className="code-link"> + This application demonstrates integrating Digi-ID authentication using the <a href="https://github.com/pawelzelawski/digiid-ts" target="_blank" rel="noopener noreferrer">digiid-ts</a> library. + </p> + <p> + Upon successful verification, the system can identify the following DigiByte address formats: + </p> + <ul> + <li>Legacy Addresses (P2PKH) - starting with 'D'</li> + <li>Pay-to-Script-Hash Addresses (P2SH) - commonly starting with 'S'</li> + <li>Segregated Witness (SegWit) Addresses - starting with 'dgb1'</li> + </ul> + </div> </div> ); } diff --git a/src/client/index.css b/src/client/index.css index 2349c9b..6cf1cfb 100644 --- a/src/client/index.css +++ b/src/client/index.css @@ -2,9 +2,24 @@ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + + /* DigiID Inspired Colors (Light Theme Defaults) */ + --digi-blue: #003366; + --digi-accent: #007bff; + --digi-text-light: #f0f0f0; + --digi-text-dark: #222222; + --digi-bg-dark: var(--digi-blue); + --digi-bg-light: #ffffff; + --digi-border-dark: #0059b3; + --digi-border-light: #cccccc; + --digi-address-bg-light: #e8f0fe; + --digi-error-light: #d9534f; + --digi-code-bg-light: #e8f0fe; + + /* Default to Light Theme */ + color-scheme: light; + color: var(--digi-text-dark); + background-color: var(--digi-bg-light); font-synthesis: none; text-rendering: optimizeLegibility; @@ -14,8 +29,6 @@ body { margin: 0; - display: flex; - place-items: center; min-width: 320px; min-height: 100vh; } @@ -23,7 +36,7 @@ body { #root { max-width: 1280px; margin: 0 auto; - padding: 2rem; + padding: 0.5rem 2rem; text-align: center; } @@ -31,85 +44,301 @@ body { display: flex; flex-direction: column; align-items: center; - gap: 1.5rem; + gap: 1rem; + padding: 20px 0 0; + margin-top: 0; } -h1 { - font-size: 2.5em; - line-height: 1.1; +.app-container h1 { + margin: 0; margin-bottom: 1rem; + color: var(--digi-blue); + font-size: 1.8em; +} + +/* Style the container that holds the changing views */ +.view-container { + width: 100%; /* Keep width if needed for centering the box */ + display: flex; /* Keep display flex for centering */ + justify-content: center; /* Center the box itself */ +} + +h1 { + font-size: 1.5em; + line-height: 1.2; + margin: 0; + color: var(--digi-blue); } button { border-radius: 8px; border: 1px solid transparent; - padding: 0.6em 1.2em; + padding: 0.5em 1em; font-size: 1em; font-weight: 500; font-family: inherit; - background-color: #1a1a1a; + background-color: var(--digi-accent); + color: var(--digi-text-light); cursor: pointer; - transition: border-color 0.25s; + transition: border-color 0.25s, background-color 0.25s; + margin-top: 0.5rem; } button:hover { - border-color: #646cff; + background-color: var(--digi-blue); + border-color: var(--digi-blue); } button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } button:disabled { - background-color: #333; + background-color: #555; /* Keep disabled distinct */ cursor: not-allowed; opacity: 0.6; } -.initial-view, .waiting-view, .success-view, .failed-view { +/* Box styling for views that need it */ +.waiting-view, .success-view, .failed-view { padding: 1.5rem; - border: 1px solid #555; + border: 1px solid var(--digi-border-light); + background-color: rgba(0, 0, 0, 0.02); border-radius: 8px; display: flex; flex-direction: column; align-items: center; + justify-content: center; gap: 1rem; - min-width: 300px; + width: 390px; + height: 560px; + box-sizing: border-box; +} + +.waiting-view { + padding: 1rem; + gap: 0.5rem; +} + +.waiting-view h2 { + margin-bottom: 0.1rem; +} + +.waiting-view p { + margin: 0.1rem 0; } .waiting-view img { - background-color: white; /* Ensure QR code background is white */ - padding: 10px; - border-radius: 4px; + margin: 0.5rem auto; +} + +.waiting-view button { + margin-top: 0.5rem; } .address { font-family: monospace; - background-color: #333; + background-color: var(--digi-address-bg-light); + color: var(--digi-text-dark); padding: 0.5em; border-radius: 4px; word-break: break-all; } .error-message { - color: #ff6b6b; + color: var(--digi-error-light); font-weight: bold; } +code, .code-link a { + background-color: var(--digi-code-bg-light); + color: var(--digi-text-dark); + padding: 0.2em 0.4em; + border-radius: 3px; + font-family: monospace; + text-decoration: none; /* Remove underline from link part */ +} + +.code-link a:hover { + text-decoration: underline; +} -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - button { - background-color: #f9f9f9; +.description { + font-size: 0.9em; + line-height: 1.4; + margin: 0; + text-align: center; + max-width: 100%; +} + +.description ul { + margin-top: 0.5em; + padding-left: 20px; +} + +.description li { + margin-bottom: 0.3em; + text-align: left; +} + +/* Base styling ONLY for view transitions */ +.view-box { + opacity: 1; + transform: scale(1); + transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out; +} + +/* Animation for success */ +.success-view { + /* Add specific styles if needed, e.g., border color */ + border-color: #28a745; /* Green border for success */ +} + +/* Checkmark Icon Styles & Animation */ +.checkmark-icon { + width: 80px; + height: 80px; + border-radius: 50%; + display: block; + stroke-width: 3; + stroke: #28a745; /* Green checkmark */ + stroke-miterlimit: 10; + margin: 10px auto; + box-shadow: inset 0px 0px 0px #ffffff; /* Start transparent */ + animation: scale .3s ease-in-out .9s both, fill .4s ease-in-out .9s both; +} + +.checkmark__circle { + stroke-dasharray: 166; + stroke-dashoffset: 166; + stroke-width: 2; + stroke-miterlimit: 10; + stroke: #7ac142; + fill: none; + animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards; +} + +.checkmark__check { + transform-origin: 50% 50%; + stroke-dasharray: 48; + stroke-dashoffset: 48; + animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards; +} + +@keyframes stroke { + 100% { + stroke-dashoffset: 0; } - .app-container { - /* Add light mode adjustments if needed */ +} + +@keyframes scale { + 0%, 100% { + transform: none; } - .initial-view, .waiting-view, .success-view, .failed-view { - border-color: #ccc; + 50% { + transform: scale3d(1.1, 1.1, 1); } - .address { - background-color: #eee; +} + +@keyframes fill { + 100% { + box-shadow: inset 0px 0px 0px 40px #7ac142; /* Fill effect */ } +} + +/* Adjust QR code size to fit better in the box */ +.qr-code { + width: 240px; + height: 240px; + margin: 0 auto; +} + +/* Ensure success animation fits */ +.success-animation { + width: 100px; + height: 100px; + margin: 0 auto; +} + +/* Ensure waiting spinner fits */ +.waiting-spinner { + width: 40px; + height: 40px; + margin: 0 auto; +} + +/* Ensure failed X mark fits */ +.failed-x { + width: 60px; + height: 60px; + margin: 0 auto; +} + +/* Remove dark mode specific overrides */ +/* @media (prefers-color-scheme: dark) { ... } */ + +/* Initial view without box */ +.initial-view { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 120px; +} + +.initial-view h2 { + margin: 0; + color: var(--digi-blue); + font-size: 1.5em; +} + +.initial-view .description { + margin-top: 0.1rem; + margin-bottom: 0.5rem; +} + +.initial-view .qr-code { + margin: 0.5rem auto; +} + +.initial-view .waiting-text { + margin: 0.25rem 0; +} + +.initial-view button { + margin-top: 0.25rem; +} + +.signin-button { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.6rem 1.2rem; + background-color: white; + color: #1a237e; + border: 1px solid var(--digi-border-light); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + transition: all 0.2s ease; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +.signin-button:hover { + background-color: #f8f9fa; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.signin-button:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +.signin-button img { + margin: 0; + width: 20px; + height: 20px; +} + +.signin-button span { + font-weight: 600; + font-size: 0.95rem; + letter-spacing: 0.3px; }
\ No newline at end of file diff --git a/src/client/utils.ts b/src/client/utils.ts index cd9c8ba..6a95f7b 100644 --- a/src/client/utils.ts +++ b/src/client/utils.ts @@ -1,20 +1,31 @@ -// Simple utility to determine DigiByte address type based on prefix +// Utility to determine DigiByte address type based on prefix -export type DigiByteAddressType = 'DigiByte (DGB)' | 'DigiAsset (DGA)' | 'Unknown'; +export type DigiByteAddressFormat = 'Legacy (P2PKH)' | 'Script (P2SH)' | 'SegWit (Bech32)' | 'Unknown'; -export function getDigiByteAddressType(address: string | undefined | null): DigiByteAddressType { +/** + * Determines the format of a DigiByte address based on its prefix. + * Note: P2SH addresses on DigiByte often start with 'S', but complex scripts + * might result in different prefixes. This function covers common cases. + * Legacy starts with 'D'. SegWit starts with 'dgb1'. + * + * @param address The DigiByte address string. + * @returns The determined address format. + */ +export function getDigiByteAddressType(address: string | undefined | null): DigiByteAddressFormat { if (!address) { return 'Unknown'; } if (address.startsWith('dgb1')) { - return 'DigiByte (DGB)'; + return 'SegWit (Bech32)'; } - // Add other prefixes if DigiAssets use a distinct one, e.g., 'dga1' - // For now, assume non-DGB is DigiAsset, but this might need refinement - // depending on actual DigiAsset address formats. - else { - // Assuming DigiAssets might start differently or be the fallback - // This is a placeholder assumption. - return 'DigiAsset (DGA)'; // Placeholder - ADJUST BASED ON ACTUAL DGA PREFIX + if (address.startsWith('S')) { // Common prefix for P2SH on DGB + return 'Script (P2SH)'; } + if (address.startsWith('D')) { + return 'Legacy (P2PKH)'; + } + + // If it doesn't match known prefixes, return Unknown + // Could potentially add DigiAsset checks here if they have distinct prefixes + return 'Unknown'; }
\ No newline at end of file |