1234import { Show, createSignal } from "solid-js";56interface AdoptionConsentModalProps {7 receiverDeviceName: string;8 sas: string;9 verifyMasterPassword(password: string): Promise<boolean>;10 onCancel(): void;11 onApprove(): Promise<void>;12}1314export function AdoptionConsentModal(props: AdoptionConsentModalProps) {15 const [password, setPassword] = createSignal("");16 const [error, setError] = createSignal("");17 const [busy, setBusy] = createSignal(false);1819 async function submit() {20 if (!password() || busy()) return;21 setBusy(true);22 setError("");23 const ok = await props.verifyMasterPassword(password());24 if (!ok) {25 setPassword("");26 setError("Incorrect password");27 setBusy(false);28 queueMicrotask(() => document.getElementById("adoption-consent-password")?.focus());29 return;30 }31 await props.onApprove();32 setBusy(false);33 }3435 return (36 <div class="peach-adoption-consent-backdrop" role="presentation">37 <section38 class="peach-adoption-consent"39 role="dialog"40 aria-modal="true"41 aria-labelledby="adoption-consent-title"42 >43 <div class="peach-adoption-consent-icon" aria-hidden="true">!</div>44 <h2 id="adoption-consent-title">Share vault with new device?</h2>45 <p class="peach-adoption-consent-device">46 "{props.receiverDeviceName}" wants to join this vault.47 </p>48 <p class="peach-adoption-consent-warning">49 This copies your entire vault including all passwords and Pro to that device.50 </p>51 <div class="peach-adoption-consent-sas">52 <span>Confirm the code matches:</span>53 <strong>{props.sas.replace(/(\d)(?=\d)/g, "$1 ")}</strong>54 </div>55 <label class="peach-label" for="adoption-consent-password">56 Enter your master password to approve57 </label>58 <input59 id="adoption-consent-password"60 class="peach-underline-input"61 type="password"62 autocomplete="current-password"63 value={password()}64 disabled={busy()}65 onInput={(event) => setPassword((event.target as HTMLInputElement).value)}66 onKeyDown={(event) => {67 if (event.key === "Enter") void submit();68 if (event.key === "Escape") props.onCancel();69 }}70 />71 <Show when={error()}>72 <p class="peach-hint peach-hint-error">{error()}</p>73 </Show>74 <div class="peach-adoption-consent-actions">75 <button76 type="button"77 class="peach-btn peach-btn--ghost peach-btn--md"78 disabled={busy()}79 onClick={props.onCancel}80 >81 Cancel82 </button>83 <button84 type="button"85 class="peach-btn peach-btn--accent peach-btn--md peach-adoption-consent-share"86 disabled={!password() || busy()}87 onClick={() => void submit()}88 >89 {busy() ? "Sharing..." : "Share vault ->"}90 </button>91 </div>92 </section>93 </div>94 );95}96979899export const ADOPTION_CONSENT_CSS = `100.peach-adoption-consent-backdrop {101 position: fixed;102 inset: 0;103 z-index: 1000;104 display: grid;105 place-items: center;106 padding: 18px;107 background: rgba(15, 10, 5, 0.72);108}109.peach-adoption-consent {110 width: min(100%, 340px);111 border: 1px solid color-mix(in srgb, #f59e0b 54%, var(--peach-border));112 border-radius: 8px;113 padding: 18px;114 background: color-mix(in srgb, #2c1b08 84%, var(--peach-surface));115 color: var(--peach-t0);116 box-shadow: 0 18px 60px rgba(0, 0, 0, 0.42);117}118.peach-adoption-consent-icon {119 width: 32px;120 height: 32px;121 display: grid;122 place-items: center;123 border-radius: 50%;124 background: #f59e0b;125 color: #1f1304;126 font-weight: 800;127}128.peach-adoption-consent h2 {129 margin: 12px 0 8px;130 font-size: 1.22rem;131 line-height: 1.15;132}133.peach-adoption-consent-device,134.peach-adoption-consent-warning {135 margin: 0 0 12px;136 color: var(--peach-t1);137 line-height: 1.35;138}139.peach-adoption-consent-warning {140 color: #ffd79a;141}142.peach-adoption-consent-sas {143 display: flex;144 justify-content: space-between;145 align-items: center;146 gap: 12px;147 margin: 14px 0;148 padding: 10px;149 border-radius: 6px;150 background: rgba(245, 158, 11, 0.12);151}152.peach-adoption-consent-sas strong {153 font-variant-numeric: tabular-nums;154 letter-spacing: 0;155}156.peach-adoption-consent-actions {157 display: flex;158 justify-content: space-between;159 gap: 10px;160 margin-top: 16px;161}162.peach-adoption-consent-actions > button {163 flex: 1;164}165`;166