1import { createSignal, onMount, onCleanup } from 'solid-js';23interface PasskeyToastProps {4 site: string;5 displayName: string;6 username: string;7 requestId: string;8 onSignIn: () => void;9 onDeny: () => void;10}1112const TIMEOUT_MS = 30_000;1314export function PasskeyToast(props: PasskeyToastProps) {15 const [visible, setVisible] = createSignal(false);16 const [exiting, setExiting] = createSignal(false);17 let timeoutId: number;1819 onMount(() => {20 requestAnimationFrame(() => setVisible(true));2122 timeoutId = window.setTimeout(() => {23 handleDeny();24 }, TIMEOUT_MS);25 });2627 onCleanup(() => {28 clearTimeout(timeoutId);29 });3031 function handleSignIn() {32 clearTimeout(timeoutId);33 exit(props.onSignIn);34 }3536 function handleDeny() {37 clearTimeout(timeoutId);38 exit(props.onDeny);39 }4041 function exit(callback: () => void) {42 setExiting(true);43 setTimeout(callback, 220);44 }4546 47 48 return (49 <div50 class={[51 'peach-passkey-toast',52 visible() ? 'is-visible' : '',53 exiting() ? 'is-exiting' : '',54 ].join(' ')}55 >56 <div class="peach-passkey-toast-body">57 <div class="peach-passkey-toast-icon">58 <KeyIcon />59 </div>6061 <div class="peach-passkey-toast-domain">{props.site}</div>6263 <h2 class="peach-passkey-toast-headline">64 sign in with passkey?65 </h2>6667 <div class="peach-passkey-toast-user">68 {props.displayName} · {props.username}69 </div>7071 <p class="peach-passkey-toast-notice">72 Peach will authenticate you with your stored passkey for {props.site}.73 </p>7475 <div class="peach-passkey-toast-countdown">76 <div77 class="peach-passkey-toast-countdown-bar"78 style={{ 'animation-duration': `${TIMEOUT_MS}ms` }}79 />80 </div>8182 <div class="peach-passkey-toast-actions">83 <button84 class="peach-passkey-toast-btn peach-passkey-toast-btn--deny"85 on:click={handleDeny}86 >87 deny88 </button>89 <button90 class="peach-passkey-toast-btn peach-passkey-toast-btn--signin"91 on:click={handleSignIn}92 >93 sign in94 </button>95 </div>96 </div>97 </div>98 );99}100101function KeyIcon() {102 return (103 <svg width="22" height="22" viewBox="0 0 22 22" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">104 <circle cx="7" cy="11" r="3.5" />105 <path d="M10.5 11h9M16.5 11v3M19.5 11v2" />106 </svg>107 );108}