Ctrl K
ComunidadGuías…

src/lib/auth-broadcast.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Tells this browser's other tabs that the session just ended here.
//
// Signing out and deleting the account both revoke the session on the server, and
// both leave the tab that did it with a page load. Every other tab would keep showing
// what it had already fetched - dives, a name, private card scans held as blob URLs -
// until its next request happened to fail. `AuthProvider` listens and leaves too.
//
// One channel per document, and it is the one that posts: `BroadcastChannel` delivers
// to every *other* instance of the name, including others in the same tab, so a
// second instance here would hear this tab's own message and race its navigation.
//
// The name carries no `opendiving:` prefix on purpose. That prefix marks browser
// storage keys, which `lib/storage-keys.test.ts` sends to the privacy page; a channel
// stores nothing, and a message nobody is listening for is simply dropped.

const CHANNEL_NAME = "auth";
const SIGNED_OUT = "signed-out";

let channel: BroadcastChannel | null | undefined;

// Lazy, and `null` where the API is missing (Safari before 15.4): the tabs then
// find out the old way, on their next failed request.
function authChannel(): BroadcastChannel | null {
  if (channel === undefined) {
    channel =
      typeof BroadcastChannel === "undefined"
        ? null
        : new BroadcastChannel(CHANNEL_NAME);
  }
  return channel;
}

/** Tells the other tabs the session has ended. Call once the server confirmed it. */
export function announceSignedOut(): void {
  authChannel()?.postMessage(SIGNED_OUT);
}

/**
 * Runs `listener` when another tab ends the session. Returns the unsubscribe.
 */
export function onSignedOutElsewhere(listener: () => void): () => void {
  const current = authChannel();
  if (!current) return () => {};
  const handle = (event: MessageEvent) => {
    if (event.data === SIGNED_OUT) listener();
  };
  current.addEventListener("message", handle);
  return () => current.removeEventListener("message", handle);
}