mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
The login modal now shows <bitlogin-auth> -- a portable Nostr identity behind a login name and password -- as the default sign-in path, styled with bitvid's own brand tokens (accent-strong, surfaces, radii). The existing NIP-07 extension, nsec, and NIP-46 remote-signer options still work exactly as before, just collapsed under a new "Other login options" fold instead of a flat "Available methods" grid; nip07 no longer carries the stale "Recommended" eyebrow now that BitLogin has that role. BitLogin isn't a single button-click-then-await-a-promise flow like the other providers -- it's a whole multi-screen sign-in/create/recover UI -- so it gets a permanent, statically-declared widget in components/login-modal.html (js/ui/bitloginModalIntegration.js relocates it, never destroys and recreates it, since removal tears down its crypto Web Worker) instead of a grid button. It still goes through the exact same session-establishment path as every other signer: a new "bitlogin" provider (js/services/authProviders/bitlogin.js) lets AuthService resolve and activate it via the normal requestLogin -> handleAuthLogin path, while providersForModal excludes it from the grid so it isn't rendered twice. Two things this needed beyond the wiring itself: - js/app.js is one line from its lint/file-size ceiling, so the only change there is renaming an import binding (providers -> providersForModal), a net-zero line change. The actual widget/event wiring lives in a new, unconstrained module instead. - The login modal's HTML (including the permanent widget element) loads asynchronously after bootstrap runs, so wireBitloginLogin() waits via MutationObserver rather than assuming a specific call order. Vendored via scripts/build-bitlogin-widget.mjs (mirrors the existing Bitcoin Connect/Blossom vendor steps): BitLogin isn't published to npm, so the widget build is copied verbatim from the sibling ../bitlogin checkout into committed vendor/bitlogin/, then lazy-imported (like Bitcoin Connect) so its ~350KB never weighs on visitors who never open the login modal. Gated behind FEATURE_BITLOGIN (config/instance-config.js), default on for this unstable-branch experiment per AGENTS.md §1 -- reset to false before this reaches main until verified end-to-end against production relays. Verified: npm run lint (clean), the full auth/login test suite (43/43, including new coverage for the provider + adapter), and a manual Chrome pass against the built dist/ -- widget renders themed correctly, "Other login options" still shows the original four methods unchanged.
117 lines
4.2 KiB
JavaScript
117 lines
4.2 KiB
JavaScript
// js/ui/bitloginModalIntegration.js
|
|
//
|
|
// Wires the permanent <bitlogin-auth id="bitloginWidget"> element -- declared
|
|
// once, statically, in components/login-modal.html, never written by an
|
|
// innerHTML re-render -- into the exact same session-establishment path every
|
|
// other sign-in method uses (AuthService.requestLogin -> handleAuthLogin).
|
|
// BitLogin isn't a single button-click-then-await-a-promise flow like the
|
|
// other providers, it's a whole multi-screen sign-in/create/recover UI, so it
|
|
// gets its own widget in the modal instead of a grid button (see
|
|
// providersForModal in js/services/authProviders/index.js) and its own small
|
|
// integration module here rather than special-casing LoginModalController.
|
|
import { devLogger } from "../utils/logger.js";
|
|
import { closeStaticModal } from "./components/staticModalAccessibility.js";
|
|
import { setModalState as setGlobalModalState } from "../state/appState.js";
|
|
import { createBitloginAdapter } from "../nostr/adapters/bitloginAdapter.js";
|
|
import { setPendingBitloginResult } from "../services/authProviders/bitlogin.js";
|
|
import { FEATURE_BITLOGIN } from "../constants.js";
|
|
|
|
// Vendored, pinned BitLogin widget bundle (scripts/build-bitlogin-widget.mjs).
|
|
// Lazy-imported so its ~350KB never weighs on the main bundle for visitors who
|
|
// never open the login modal, mirroring the existing Bitcoin Connect vendor
|
|
// step (js/ui/profileModal/ProfileWalletController.js). A plain dynamic
|
|
// import is enough: the bundle's only job is the customElements.define("bitlogin-auth")
|
|
// side effect, which retroactively upgrades the already-present <bitlogin-auth>
|
|
// tag in the modal's static HTML once it resolves.
|
|
const BITLOGIN_WIDGET_BUNDLE_URL = "../../vendor/bitlogin/bitlogin.js";
|
|
|
|
let widgetLoadPromise = null;
|
|
|
|
function loadBitloginWidgetOnce() {
|
|
if (!widgetLoadPromise) {
|
|
widgetLoadPromise = import(BITLOGIN_WIDGET_BUNDLE_URL).catch((error) => {
|
|
widgetLoadPromise = null;
|
|
throw error;
|
|
});
|
|
}
|
|
return widgetLoadPromise;
|
|
}
|
|
|
|
function closeLoginModal() {
|
|
const modal = document.getElementById("loginModal");
|
|
if (modal && closeStaticModal(modal)) {
|
|
setGlobalModalState("login", false);
|
|
}
|
|
}
|
|
|
|
function attachWidget(app, widget) {
|
|
const mount = document.getElementById("bitloginMount");
|
|
if (!FEATURE_BITLOGIN) {
|
|
// Unstable-branch experiment (AGENTS.md §1, config/instance-config.js) --
|
|
// leave the permanent, statically-declared markup in place but never load
|
|
// the vendored bundle or show the (otherwise-empty, unupgraded) element.
|
|
if (mount instanceof HTMLElement) {
|
|
mount.hidden = true;
|
|
}
|
|
return;
|
|
}
|
|
if (mount instanceof HTMLElement) {
|
|
mount.hidden = false;
|
|
}
|
|
|
|
loadBitloginWidgetOnce().catch((error) => {
|
|
devLogger.warn("[BitLogin] Failed to load the widget bundle:", error);
|
|
});
|
|
|
|
widget.addEventListener("bitlogin-login", async (event) => {
|
|
const pubkey =
|
|
event?.detail && typeof event.detail.publicKey === "string"
|
|
? event.detail.publicKey.trim()
|
|
: "";
|
|
if (!pubkey) {
|
|
return;
|
|
}
|
|
|
|
setPendingBitloginResult({
|
|
pubkey,
|
|
signer: createBitloginAdapter(widget, pubkey),
|
|
});
|
|
|
|
try {
|
|
await app.authService.requestLogin({ providerId: "bitlogin" });
|
|
closeLoginModal();
|
|
} catch (error) {
|
|
devLogger.warn("[BitLogin] Sign-in failed to apply:", error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Call once during app bootstrap, after `app.authService` exists. The login
|
|
* modal's HTML (including the permanent #bitloginWidget element) loads
|
|
* asynchronously (js/index.js bootstrapInterface()) and isn't guaranteed to be
|
|
* in the DOM yet at this point, so this waits for it rather than requiring a
|
|
* specific call order between the two.
|
|
* @param {{ authService: { requestLogin: Function } }} app
|
|
*/
|
|
export function wireBitloginLogin(app) {
|
|
if (!app?.authService) {
|
|
return;
|
|
}
|
|
|
|
const existing = document.getElementById("bitloginWidget");
|
|
if (existing instanceof HTMLElement) {
|
|
attachWidget(app, existing);
|
|
return;
|
|
}
|
|
|
|
const observer = new MutationObserver(() => {
|
|
const widget = document.getElementById("bitloginWidget");
|
|
if (widget instanceof HTMLElement) {
|
|
observer.disconnect();
|
|
attachWidget(app, widget);
|
|
}
|
|
});
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
}
|