Add sign-up via Nstart
This commit is contained in:
parent
03395adf85
commit
bcbc70ecb8
3 changed files with 104 additions and 3 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -11,6 +11,7 @@
|
||||||
"@djot/djot": "^0.3.2",
|
"@djot/djot": "^0.3.2",
|
||||||
"@nostr/gadgets": "npm:@jsr/nostr__gadgets@^0.1.8",
|
"@nostr/gadgets": "npm:@jsr/nostr__gadgets@^0.1.8",
|
||||||
"@nostr/tools": "npm:@jsr/nostr__tools@^2.23.3",
|
"@nostr/tools": "npm:@jsr/nostr__tools@^2.23.3",
|
||||||
|
"nstart-modal": "^2.1.0",
|
||||||
"uqr": "^0.1.3"
|
"uqr": "^0.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -3170,6 +3171,11 @@
|
||||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/nstart-modal": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nstart-modal/-/nstart-modal-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-PolShYoWK07yJJbINtUn/IoOI5B0lmXRG9zOY9dirKKVjmMWFKjYLlafunNOl94EGcEndzAPWJAFDCt8flYRqg=="
|
||||||
|
},
|
||||||
"node_modules/obug": {
|
"node_modules/obug": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
"@djot/djot": "^0.3.2",
|
"@djot/djot": "^0.3.2",
|
||||||
"@nostr/gadgets": "npm:@jsr/nostr__gadgets@^0.1.8",
|
"@nostr/gadgets": "npm:@jsr/nostr__gadgets@^0.1.8",
|
||||||
"@nostr/tools": "npm:@jsr/nostr__tools@^2.23.3",
|
"@nostr/tools": "npm:@jsr/nostr__tools@^2.23.3",
|
||||||
|
"nstart-modal": "^2.1.0",
|
||||||
"uqr": "^0.1.3"
|
"uqr": "^0.1.3"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
loginWithNostrConnect,
|
loginWithNostrConnect,
|
||||||
loginWithNsec,
|
loginWithNsec,
|
||||||
} from "$lib/auth.svelte";
|
} from "$lib/auth.svelte";
|
||||||
|
import { TITLE, MODE } from "$lib/config";
|
||||||
|
import { groupStore } from "$lib/group.svelte";
|
||||||
|
import { themeState } from "$lib/theme.svelte";
|
||||||
|
import type { NstartModal } from "nstart-modal";
|
||||||
import { onDestroy, tick } from "svelte";
|
import { onDestroy, tick } from "svelte";
|
||||||
import { renderSVG } from "uqr";
|
import { renderSVG } from "uqr";
|
||||||
|
|
||||||
|
|
@ -21,6 +25,19 @@
|
||||||
let nsecInput = $state<HTMLInputElement | null>(null);
|
let nsecInput = $state<HTMLInputElement | null>(null);
|
||||||
let bunkerInput = $state<HTMLInputElement | null>(null);
|
let bunkerInput = $state<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
// Nstart sign-up wizard (an iframe overlay). Built lazily and reused; it is
|
||||||
|
// rebuilt only when the theme it was built for changes.
|
||||||
|
let wizard: NstartModal | null = null;
|
||||||
|
let wizardTheme: string | null = null;
|
||||||
|
// The login dialog hides while the wizard is up and comes back on cancel
|
||||||
|
let signingUp = $state(false);
|
||||||
|
|
||||||
|
const appName = $derived(
|
||||||
|
TITLE ||
|
||||||
|
(MODE === "simple" ? groupStore.data?.name : "") ||
|
||||||
|
(typeof location !== "undefined" ? location.host : ""),
|
||||||
|
);
|
||||||
|
|
||||||
// Client-initiated NIP-46 flow, alive only while the bunker view is shown
|
// Client-initiated NIP-46 flow, alive only while the bunker view is shown
|
||||||
let connect = $state<ReturnType<typeof loginWithNostrConnect> | null>(null);
|
let connect = $state<ReturnType<typeof loginWithNostrConnect> | null>(null);
|
||||||
const qrSvg = $derived(
|
const qrSvg = $derived(
|
||||||
|
|
@ -131,6 +148,62 @@
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The accent color as Nstart wants it (hex, no #), so the wizard matches the
|
||||||
|
// forum; env overrides are already applied to the CSS variable.
|
||||||
|
function accentHex(): string | undefined {
|
||||||
|
const v = getComputedStyle(document.documentElement)
|
||||||
|
.getPropertyValue("--color-accent")
|
||||||
|
.trim();
|
||||||
|
return /^#[0-9a-fA-F]{6}$/.test(v) ? v.slice(1) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSignup() {
|
||||||
|
if (busy) return;
|
||||||
|
error = null;
|
||||||
|
stopConnect();
|
||||||
|
const { NstartModal } = await import("nstart-modal");
|
||||||
|
if (!wizard || wizardTheme !== themeState.theme) {
|
||||||
|
wizard?.destroy();
|
||||||
|
wizardTheme = themeState.theme;
|
||||||
|
wizard = new NstartModal({
|
||||||
|
baseUrl: "https://nstart.me",
|
||||||
|
an: appName,
|
||||||
|
aa: accentHex(),
|
||||||
|
am: themeState.theme,
|
||||||
|
// Only bunker:// or nsec come back; ncryptsec has no login here
|
||||||
|
aac: true,
|
||||||
|
onComplete: ({ nostrLogin }) => void completeSignup(nostrLogin),
|
||||||
|
onCancel: () => {
|
||||||
|
signingUp = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
signingUp = true;
|
||||||
|
wizard.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function completeSignup(nostrLogin: string | null) {
|
||||||
|
signingUp = false;
|
||||||
|
if (!nostrLogin) {
|
||||||
|
error = "Sign-up finished without a login credential";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
busy = true;
|
||||||
|
try {
|
||||||
|
if (nostrLogin.startsWith("bunker://")) {
|
||||||
|
await loginWithBunker(nostrLogin);
|
||||||
|
} else {
|
||||||
|
await loginWithNsec(nostrLogin);
|
||||||
|
}
|
||||||
|
closeLogin();
|
||||||
|
reset();
|
||||||
|
} catch (e) {
|
||||||
|
error = e instanceof Error ? e.message : "Failed to login";
|
||||||
|
} finally {
|
||||||
|
busy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function showNsecView() {
|
async function showNsecView() {
|
||||||
stopConnect();
|
stopConnect();
|
||||||
view = "nsec";
|
view = "nsec";
|
||||||
|
|
@ -154,16 +227,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(e: KeyboardEvent) {
|
function onKeydown(e: KeyboardEvent) {
|
||||||
if (!auth.loginModalOpen) return;
|
if (!auth.loginModalOpen || signingUp) return;
|
||||||
if (e.key === "Escape") onClose();
|
if (e.key === "Escape") onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(stopConnect);
|
onDestroy(() => {
|
||||||
|
stopConnect();
|
||||||
|
wizard?.destroy();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window onkeydown={onKeydown} />
|
<svelte:window onkeydown={onKeydown} />
|
||||||
|
|
||||||
{#if auth.loginModalOpen}
|
{#if auth.loginModalOpen && !signingUp}
|
||||||
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -230,6 +306,24 @@
|
||||||
>
|
>
|
||||||
Or log in using your nsec
|
Or log in using your nsec
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="my-5 border-t border-neutral-200 dark:border-neutral-700"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<p
|
||||||
|
class="mb-3 text-center text-sm text-neutral-600 dark:text-neutral-400"
|
||||||
|
>
|
||||||
|
Don't have an account yet?
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={handleSignup}
|
||||||
|
disabled={busy}
|
||||||
|
class="border-accent text-accent hover:bg-accent/10 w-full rounded border px-3 py-2 text-sm font-medium disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Sign up
|
||||||
|
</button>
|
||||||
{:else if view === "bunker"}
|
{:else if view === "bunker"}
|
||||||
{#if connect}
|
{#if connect}
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue