From cd1b328b1b8f34af526829ee615e72dc061d9b8c Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 17 Dec 2025 18:43:13 -0800 Subject: [PATCH] Add pomade signing --- .env.template | 2 +- package.json | 18 +- src/app/components/AppContainer.svelte | 21 +-- src/app/components/EmailConfirm.svelte | 52 ------ src/app/components/LogIn.svelte | 40 ++--- src/app/components/LogInEmail.svelte | 115 +++++++++++++ ...ordResetRequest.svelte => LogInOTP.svelte} | 56 +++---- src/app/components/LogInOTPConfirm.svelte | 127 ++++++++++++++ src/app/components/LogInPassword.svelte | 156 ------------------ src/app/components/PasswordReset.svelte | 73 -------- src/app/components/ProfileCircles.svelte | 19 ++- src/app/components/ProfileEject.svelte | 29 +--- src/app/components/SignUp.svelte | 84 ++-------- src/app/components/SignUpEmail.svelte | 141 ++++++++++++++++ src/app/components/SignUpEmailConfirm.svelte | 83 ++++++++++ src/app/components/SignUpProfile.svelte | 10 +- src/app/components/SignUpSuccess.svelte | 18 -- src/app/components/SpaceEdit.svelte | 2 +- src/app/components/SpaceJoin.svelte | 12 +- src/app/core/state.ts | 11 +- 20 files changed, 582 insertions(+), 487 deletions(-) delete mode 100644 src/app/components/EmailConfirm.svelte create mode 100644 src/app/components/LogInEmail.svelte rename src/app/components/{PasswordResetRequest.svelte => LogInOTP.svelte} (57%) create mode 100644 src/app/components/LogInOTPConfirm.svelte delete mode 100644 src/app/components/LogInPassword.svelte delete mode 100644 src/app/components/PasswordReset.svelte create mode 100644 src/app/components/SignUpEmail.svelte create mode 100644 src/app/components/SignUpEmailConfirm.svelte delete mode 100644 src/app/components/SignUpSuccess.svelte diff --git a/.env.template b/.env.template index 801d6343..48bac14c 100644 --- a/.env.template +++ b/.env.template @@ -1,6 +1,6 @@ VITE_DEFAULT_PUBKEYS=06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71,266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5,391819e2f2f13b90cac7209419eb574ef7c0d1f4e81867fc24c47a3ce5e8a248,3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,55f04590674f3648f4cdc9dc8ce32da2a282074cd0b020596ee033d12d385185,58c741aa630c2da35a56a77c1d05381908bd10504fdd2d8b43f725efa6d23196,61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9,6389be6491e7b693e9f368ece88fcd145f07c068d2c1bbae4247b9b5ef439d32,63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed,6e75f7972397ca3295e0f4ca0fbc6eb9cc79be85bafdd56bd378220ca8eee74e,76c71aae3a491f1d9eec47cba17e229cda4113a0bbb6e6ae1776d7643e29cafa,7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322,b676ded7c768d66a757aa3967b1243d90bf57afb09d1044d3219d8d424e4aea0,dace63b00c42e6e017d00dd190a9328386002ff597b841eb5ef91de4f1ce8491,eeb11961b25442b16389fe6c7ebea9adf0ac36dd596816ea7119e521b8821b9e,fe7f6bc6f7338b76bbf80db402ade65953e20b2f23e66e898204b63cc42539a3 VITE_DEFAULT_BLOSSOM_SERVERS=https://blossom.primal.net/ -VITE_BURROW_URL= +VITE_POMADE_SIGNERS=92f258a6e1baf021408e4fe9f8ec526a93397a3616b52bd64d88ef51433a4e53,c00b809f52cdeca7e8fb7e91d5d9526a3fa44ff8e513d92e5abac3a26d88f89f,98d9454a5ef68d6b9973831a969cd564447263b4e6d27c7efe7909c40c3b4825 VITE_PLATFORM_URL=https://app.flotilla.social VITE_PLATFORM_TERMS=https://flotilla.social/terms VITE_PLATFORM_PRIVACY=https://flotilla.social/privacy diff --git a/package.json b/package.json index 7b81151a..9c14f4b7 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,8 @@ "qr-scanner": "^1.4.2", "qrcode": "^1.5.4", "throttle-debounce": "^5.0.2", - "tippy.js": "^6.3.7" + "tippy.js": "^6.3.7", + "@pomade/core": "^0.0.5" }, "pnpm": { "ignoredBuiltDependencies": [ @@ -94,6 +95,19 @@ ], "onlyBuiltDependencies": [ "sharp" - ] + ], + "overrides": { + "@welshman/app": "link:../welshman/packages/app", + "@welshman/content": "link:../welshman/packages/content", + "@welshman/editor": "link:../welshman/packages/editor", + "@welshman/feeds": "link:../welshman/packages/feeds", + "@welshman/lib": "link:../welshman/packages/lib", + "@welshman/net": "link:../welshman/packages/net", + "@welshman/router": "link:../welshman/packages/router", + "@welshman/signer": "link:../welshman/packages/signer", + "@welshman/store": "link:../welshman/packages/store", + "@welshman/util": "link:../welshman/packages/util", + "@pomade/core": "link:../pomade/packages/core" + } } } diff --git a/src/app/components/AppContainer.svelte b/src/app/components/AppContainer.svelte index 7fc7a290..598746b2 100644 --- a/src/app/components/AppContainer.svelte +++ b/src/app/components/AppContainer.svelte @@ -5,32 +5,13 @@ import Landing from "@app/components/Landing.svelte" import Toast from "@app/components/Toast.svelte" import PrimaryNav from "@app/components/PrimaryNav.svelte" - import EmailConfirm from "@app/components/EmailConfirm.svelte" - import PasswordReset from "@app/components/PasswordReset.svelte" - import {BURROW_URL} from "@app/core/state" - import {modals, pushModal} from "@app/util/modal" + import {modals} from "@app/util/modal" interface Props { children: Snippet } const {children}: Props = $props() - - if (BURROW_URL && !$pubkey) { - if ($page.url.pathname === "/confirm-email") { - pushModal(EmailConfirm, { - email: $page.url.searchParams.get("email"), - confirm_token: $page.url.searchParams.get("confirm_token"), - }) - } - - if ($page.url.pathname === "/reset-password") { - pushModal(PasswordReset, { - email: $page.url.searchParams.get("email"), - reset_token: $page.url.searchParams.get("reset_token"), - }) - } - }
diff --git a/src/app/components/EmailConfirm.svelte b/src/app/components/EmailConfirm.svelte deleted file mode 100644 index c7c51cc2..00000000 --- a/src/app/components/EmailConfirm.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - -
-

- {#if loading} - Just a second... - {:else if error} - Oops! - {:else} - Success! - {/if} -

-

- - {#if loading} - Hang tight, we're checking your confirmation link. - {:else if error} - Looks like something went wrong. {error} - {:else} - You're all set - click below to log in. - {/if} - -

- -
diff --git a/src/app/components/LogIn.svelte b/src/app/components/LogIn.svelte index bae9c1f2..bc94b34e 100644 --- a/src/app/components/LogIn.svelte +++ b/src/app/components/LogIn.svelte @@ -4,7 +4,7 @@ import {getNip07, getNip55, Nip55Signer} from "@welshman/signer" import {addSession, type Session, makeNip07Session, makeNip55Session} from "@welshman/app" import Widget from "@assets/icons/widget-2.svg?dataurl" - import Key from "@assets/icons/key-minimalistic.svg?dataurl" + import Letter from "@assets/icons/letter.svg?dataurl" import Cpu from "@assets/icons/cpu-bolt.svg?dataurl" import Compass from "@assets/icons/compass-big.svg?dataurl" import Icon from "@lib/components/Icon.svelte" @@ -13,15 +13,17 @@ import SignUp from "@app/components/SignUp.svelte" import InfoNostr from "@app/components/InfoNostr.svelte" import LogInBunker from "@app/components/LogInBunker.svelte" - import LogInPassword from "@app/components/LogInPassword.svelte" + import LogInEmail from "@app/components/LogInEmail.svelte" import {pushModal, clearModals} from "@app/util/modal" - import {PLATFORM_NAME, BURROW_URL} from "@app/core/state" + import {PLATFORM_NAME, POMADE_SIGNERS} from "@app/core/state" import {pushToast} from "@app/util/toast" import {setChecked} from "@app/util/notifications" let signers: any[] = $state([]) let loading: string | undefined = $state() + const hasPomade = POMADE_SIGNERS.length >= 3 + const disabled = $derived(loading ? true : undefined) const signUp = () => pushModal(SignUp) @@ -72,7 +74,7 @@ } } - const loginWithPassword = () => pushModal(LogInPassword) + const loginWithEmail = () => pushModal(LogInEmail) const loginWithBunker = () => pushModal(LogInBunker) @@ -112,39 +114,31 @@ Log in with {app.name} {/each} - {#if BURROW_URL && !hasSigner} - {/if} - {#if BURROW_URL && hasSigner} - {/if} - {#if !hasSigner || !BURROW_URL} + {#if !hasSigner || !hasPomade} + class="btn {hasSigner || hasPomade ? '' : 'btn-neutral'}"> Browse Signer Apps diff --git a/src/app/components/LogInEmail.svelte b/src/app/components/LogInEmail.svelte new file mode 100644 index 00000000..ab4134c6 --- /dev/null +++ b/src/app/components/LogInEmail.svelte @@ -0,0 +1,115 @@ + + +
+ + {#snippet title()} +
Log In
+ {/snippet} + {#snippet info()} +
Log in using your email and password
+ {/snippet} +
+ + {#snippet label()} +

Email*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + {#snippet label()} +

Password*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+

+ Forgot your password? . +

+ + + + +
diff --git a/src/app/components/PasswordResetRequest.svelte b/src/app/components/LogInOTP.svelte similarity index 57% rename from src/app/components/PasswordResetRequest.svelte rename to src/app/components/LogInOTP.svelte index ab3c93c4..d8876d38 100644 --- a/src/app/components/PasswordResetRequest.svelte +++ b/src/app/components/LogInOTP.svelte @@ -1,24 +1,24 @@ + +
+ + {#snippet title()} +
Log In
+ {/snippet} + {#snippet info()} +
Enter the one-time login code sent to your email
+ {/snippet} +
+ + {#snippet label()} +

Login Code #1*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + {#snippet label()} +

Login Code #2*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + {#snippet label()} +

Login Code #3*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+

+ To keep your key as safe a possible, you will receive three separate emails. + Be sure to enter all three codes! +

+ + + + +
diff --git a/src/app/components/LogInPassword.svelte b/src/app/components/LogInPassword.svelte deleted file mode 100644 index 12244673..00000000 --- a/src/app/components/LogInPassword.svelte +++ /dev/null @@ -1,156 +0,0 @@ - - -
- - {#snippet title()} -
Log In
- {/snippet} - {#snippet info()} -
Log in using your email and password
- {/snippet} -
- - {#snippet label()} -

Email

- {/snippet} - {#snippet input()} - - {/snippet} -
- - {#snippet label()} -

Password

- {/snippet} - {#snippet input()} - - {/snippet} -
-

- Your email and password only work to log in to {PLATFORM_NAME}. To use your key on other nostr - applications, visit your settings page. -

- - - - -
diff --git a/src/app/components/PasswordReset.svelte b/src/app/components/PasswordReset.svelte deleted file mode 100644 index 05f6b8ea..00000000 --- a/src/app/components/PasswordReset.svelte +++ /dev/null @@ -1,73 +0,0 @@ - - -
- - {#snippet title()} -
Reset your password
- {/snippet} -
- - {#snippet label()} -

Email Address

- {/snippet} - {#snippet input()} - - {/snippet} -
- - {#snippet label()} -

New Password

- {/snippet} - {#snippet input()} - - {/snippet} -
- -
diff --git a/src/app/components/ProfileCircles.svelte b/src/app/components/ProfileCircles.svelte index 0b9a81ea..d9370c93 100644 --- a/src/app/components/ProfileCircles.svelte +++ b/src/app/components/ProfileCircles.svelte @@ -1,5 +1,5 @@
- {#each pubkeys - .filter(p => getProfile(p)?.picture) - .toSorted() - .slice(0, 15) as pubkey (pubkey)} -
+ {#each visiblePubkeys.toSorted().slice(0, 15) as pubkey (pubkey)} +
{/each} diff --git a/src/app/components/ProfileEject.svelte b/src/app/components/ProfileEject.svelte index 0f9193b4..514b9671 100644 --- a/src/app/components/ProfileEject.svelte +++ b/src/app/components/ProfileEject.svelte @@ -1,5 +1,4 @@ @@ -85,7 +66,7 @@ {#snippet input()} {/snippet} @@ -97,7 +78,7 @@ Go back {#if success} - diff --git a/src/app/components/SignUp.svelte b/src/app/components/SignUp.svelte index 231619aa..7af9c3af 100644 --- a/src/app/components/SignUp.svelte +++ b/src/app/components/SignUp.svelte @@ -1,95 +1,37 @@ -
+

Sign up with Nostr

{PLATFORM_NAME} is built using the , which gives users control over their digital identity using cryptographic key pairs.

- {#if BURROW_URL} - - {#snippet label()} -

Email

- {/snippet} - {#snippet input()} - - {/snippet} -
- - {#snippet label()} -

Password

- {/snippet} - {#snippet input()} - - {/snippet} -
- -

- Note that your email and password will only work to log in to {PLATFORM_NAME}. To use your key - on other nostr applications, you can create a nostr key yourself, or export your key from {PLATFORM_NAME} - later. -

- Or {/if} - @@ -97,4 +39,4 @@ Already have an account?
-
+
diff --git a/src/app/components/SignUpEmail.svelte b/src/app/components/SignUpEmail.svelte new file mode 100644 index 00000000..f6a45709 --- /dev/null +++ b/src/app/components/SignUpEmail.svelte @@ -0,0 +1,141 @@ + + +
+ + {#snippet title()} +
Sign up with Email
+ {/snippet} + {#snippet info()} +
Keep your keys safe using multi-signer key sharing
+ {/snippet} +
+

+ Under the hood, nostr uses "cryptographic keypairs" to help you prove that your identity is + actually you. +

+

+ If you you're not ready to take control of your keys though, that's ok! We'll keep them safe + until you are. +

+ + {#snippet label()} +

Email*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + {#snippet label()} +

Password*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + + + +
diff --git a/src/app/components/SignUpEmailConfirm.svelte b/src/app/components/SignUpEmailConfirm.svelte new file mode 100644 index 00000000..7e836148 --- /dev/null +++ b/src/app/components/SignUpEmailConfirm.svelte @@ -0,0 +1,83 @@ + + +
+ + {#snippet title()} +
Verify your Email Address
+ {/snippet} + {#snippet info()} +
Enter the one-time confirmation code sent to your email
+ {/snippet} +
+ + {#snippet label()} +

Confirmation Code*

+ {/snippet} + {#snippet input()} + + {/snippet} +
+

+ We just sent a one-time confirmation code to {email}. Once you receive it, you can enter it above. +

+ + + + +
diff --git a/src/app/components/SignUpProfile.svelte b/src/app/components/SignUpProfile.svelte index fdf138e5..4e51cf3f 100644 --- a/src/app/components/SignUpProfile.svelte +++ b/src/app/components/SignUpProfile.svelte @@ -8,8 +8,15 @@ import ModalFooter from "@lib/components/ModalFooter.svelte" import ProfileEditForm from "@app/components/ProfileEditForm.svelte" import SignUpKey from "@app/components/SignUpKey.svelte" + import SignUpEmail from "@app/components/SignUpEmail.svelte" import {pushModal} from "@app/util/modal" + type Props = { + flow: "nostr" | "email" + } + + const {flow}: Props = $props() + const initialValues = { profile: makeProfile(), shouldBroadcast: false, @@ -17,7 +24,8 @@ const back = () => history.back() - const onsubmit = (values: {profile: Profile}) => pushModal(SignUpKey, values) + const onsubmit = (values: {profile: Profile}) => + pushModal(flow === "nostr" ? SignUpKey : SignUpEmail, values)
diff --git a/src/app/components/SignUpSuccess.svelte b/src/app/components/SignUpSuccess.svelte deleted file mode 100644 index 7f81c39e..00000000 --- a/src/app/components/SignUpSuccess.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -
-

Success!

-

- A confirmation email has been sent to {email}. -

-

Once you've confirmed your account you'll be redirected to the login page.

- -
diff --git a/src/app/components/SpaceEdit.svelte b/src/app/components/SpaceEdit.svelte index 16dd5a6d..1ec358ef 100644 --- a/src/app/components/SpaceEdit.svelte +++ b/src/app/components/SpaceEdit.svelte @@ -24,7 +24,7 @@ initialValues: RelayProfile } - const {url, initialValues}: Props = $props() + const {url, initialValues = {}}: Props = $props() const values = $state(initialValues) diff --git a/src/app/components/SpaceJoin.svelte b/src/app/components/SpaceJoin.svelte index 5823c62b..21ad0999 100644 --- a/src/app/components/SpaceJoin.svelte +++ b/src/app/components/SpaceJoin.svelte @@ -15,18 +15,14 @@ const back = () => history.back() - const tryJoin = async () => { - await addSpaceMembership(url) - - broadcastUserData([url]) - clearModals() - } - const join = async () => { loading = true try { - await tryJoin() + await addSpaceMembership(url) + + broadcastUserData([url]) + clearModals() } catch (e) { loading = false } diff --git a/src/app/core/state.ts b/src/app/core/state.ts index d1d87f7a..3ac2c7b3 100644 --- a/src/app/core/state.ts +++ b/src/app/core/state.ts @@ -1,4 +1,5 @@ import twColors from "tailwindcss/colors" +import {context as pomadeContext} from "@pomade/core" import {Capacitor} from "@capacitor/core" import {get, derived, readable, writable} from "svelte/store" import * as nip19 from "nostr-tools/nip19" @@ -163,7 +164,7 @@ export const PLATFORM_DESCRIPTION = import.meta.env.VITE_PLATFORM_DESCRIPTION export const DEFAULT_BLOSSOM_SERVERS = fromCsv(import.meta.env.VITE_DEFAULT_BLOSSOM_SERVERS) -export const BURROW_URL = import.meta.env.VITE_BURROW_URL +export const POMADE_SIGNERS = fromCsv(import.meta.env.VITE_POMADE_SIGNERS) export const DEFAULT_PUBKEYS = import.meta.env.VITE_DEFAULT_PUBKEYS @@ -233,6 +234,10 @@ export const deriveRelaySignedEvents = (url: string, filters: Filter[]) => // Context +pomadeContext.setSignerPubkeys(POMADE_SIGNERS) + +pomadeContext.setArgonWorker(import('@pomade/core/argon-worker.js?worker')) + appContext.dufflepudUrl = DUFFLEPUD_URL routerContext.getIndexerRelays = always(INDEXER_RELAYS) @@ -516,9 +521,9 @@ export const roomsByUrl = derived(roomMetaEventsByIdByUrl, roomMetaEventsByIdByU } for (const event of metaEvents) { - const meta = readRoomMeta(event) + const meta = tryCatch(() => readRoomMeta(event)) - if (gt(deletedByH.get(meta.h), meta.event.created_at)) { + if (!meta || gt(deletedByH.get(meta.h), meta.event.created_at)) { continue }