inject.tsx

2.6 KB · raw

1import { render } from 'solid-js/web';2import { PasskeyToast } from './PasskeyToast';3// ?inline imports the CSS as a string so we can inject it into the shadow root4// rather than letting Vite/crx inject it into the page document head.5import styles from './passkey-toast.css?inline';67let currentDispose: (() => void) | null = null;8let currentRequestId: string | null = null;9let toastContainer: HTMLElement | null = null;10let shadowRoot: ShadowRoot | null = null;1112function ensureContainer(): ShadowRoot {13  if (!toastContainer) {14    toastContainer = document.createElement('div');15    toastContainer.id = 'peach-passkey-toast-root';16    shadowRoot = toastContainer.attachShadow({ mode: 'closed' });17    document.body.appendChild(toastContainer);1819    const style = document.createElement('style');20    style.textContent = styles;21    shadowRoot.appendChild(style);22  }23  return shadowRoot!;24}2526export function showPasskeyToast(payload: {27  site: string;28  displayName: string;29  username: string;30  requestId: string;31}) {32  // Dismiss any existing toast, actively denying the request it holds.33  if (currentDispose) {34    dismissPasskeyToast(currentRequestId ?? undefined);35  }3637  const root = ensureContainer();38  const wrapper = document.createElement('div');39  root.appendChild(wrapper);4041  let dispose: (() => void) | null = null;4243  dispose = render(() => (44    <PasskeyToast45      {...payload}46      onSignIn={() => {47        chrome.runtime.sendMessage({48          type: 'PASSKEY_AUTH_RESPONSE',49          payload: { requestId: payload.requestId, approved: true },50        });51        cleanup();52      }}53      onDeny={() => {54        chrome.runtime.sendMessage({55          type: 'PASSKEY_AUTH_RESPONSE',56          payload: { requestId: payload.requestId, approved: false },57        });58        cleanup();59      }}60    />61  ), wrapper);6263  currentDispose = dispose;64  currentRequestId = payload.requestId;6566  function cleanup() {67    if (dispose) {68      dispose();69      dispose = null;70    }71    wrapper.remove();72    currentDispose = null;73    currentRequestId = null;74  }75}7677export function dismissPasskeyToast(denyRequestId?: string) {78  if (denyRequestId) {79    chrome.runtime.sendMessage({80      type: 'PASSKEY_AUTH_RESPONSE',81      payload: { requestId: denyRequestId, approved: false },82    });83  }84  if (currentDispose) {85    currentDispose();86    currentDispose = null;87    currentRequestId = null;88  }89}9091chrome.runtime.onMessage.addListener((msg) => {92  if (msg.type === 'SHOW_PASSKEY_TOAST') {93    showPasskeyToast(msg.payload);94  }95  if (msg.type === 'DISMISS_PASSKEY_TOAST') {96    dismissPasskeyToast();97  }98});