diff --git a/.gitignore b/.gitignore
index b8bf853..6b647e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ pnpm-lock.yaml
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
+.vscode
diff --git a/package-lock.json b/package-lock.json
index b449d3e..839f3ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,15 +8,20 @@
"name": "cofabricate",
"version": "0.0.1",
"dependencies": {
+ "@noble/hashes": "^1.4.0",
"@nostr-dev-kit/ndk": "^2.8.2",
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
- "@nostr-dev-kit/ndk-svelte-components": "^2.2.16"
+ "@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
+ "markdown-it": "^14.1.0",
+ "sanitize-html": "^2.13.0"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "^8.56.7",
+ "@types/markdown-it": "^14.1.1",
+ "@types/sanitize-html": "^2.11.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
@@ -1212,11 +1217,42 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "dev": true
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==",
+ "dev": true,
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "dev": true
+ },
"node_modules/@types/pug": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA=="
},
+ "node_modules/@types/sanitize-html": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz",
+ "integrity": "sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==",
+ "dev": true,
+ "dependencies": {
+ "htmlparser2": "^8.0.0"
+ }
+ },
"node_modules/@types/unist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
@@ -1626,8 +1662,7 @@
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/aria-query": {
"version": "5.3.0",
@@ -3082,6 +3117,14 @@
"node": ">=10"
}
},
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
"node_modules/local-pkg": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",
@@ -3149,6 +3192,22 @@
"@jridgewell/sourcemap-codec": "^1.4.15"
}
},
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
"node_modules/marked": {
"version": "9.1.6",
"resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
@@ -3192,6 +3251,11 @@
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
},
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -3829,6 +3893,14 @@
"node": ">=6"
}
},
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -4591,6 +4663,11 @@
"resolved": "https://registry.npmjs.org/typescript-lru-cache/-/typescript-lru-cache-2.0.0.tgz",
"integrity": "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA=="
},
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="
+ },
"node_modules/ufo": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz",
diff --git a/package.json b/package.json
index 99ae03a..152518b 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
- "dev": "vite dev",
+ "dev": "vite dev --host 0.0.0.0 --port 8091",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
diff --git a/src/lib/components/Avatar.svelte b/src/lib/components/Avatar.svelte
index 91f0ff0..b063bb8 100644
--- a/src/lib/components/Avatar.svelte
+++ b/src/lib/components/Avatar.svelte
@@ -1,23 +1,35 @@
-
+
+ {#if !_ndk.activeUser}
+
+ {:else}
+
+ {/if}
+
diff --git a/src/lib/components/LoginModal/Modal.svelte b/src/lib/components/LoginModal/Modal.svelte
index f74aa1f..7fca8e6 100644
--- a/src/lib/components/LoginModal/Modal.svelte
+++ b/src/lib/components/LoginModal/Modal.svelte
@@ -1,8 +1,13 @@
@@ -11,8 +16,10 @@
diff --git a/src/lib/components/LoginModal/Nip07Button.svelte b/src/lib/components/LoginModal/Nip07Button.svelte
index b487563..c3fa47f 100644
--- a/src/lib/components/LoginModal/Nip07Button.svelte
+++ b/src/lib/components/LoginModal/Nip07Button.svelte
@@ -2,18 +2,27 @@
import { ndk } from '$lib/stores/nostr';
import { login } from '$lib/utils/login';
import { currentUser } from '$lib/store';
+ import { browser } from '$app/environment';
+ import { createEventDispatcher } from 'svelte';
let noNip07: boolean;
- $: noNip07 = !window.nostr;
+ $: if (browser) {
+ noNip07 = !window.nostr;
+ }
+
+ const dispatch = createEventDispatcher();
async function nip07Login() {
const user = await login($ndk, undefined, 'nip07');
if (!user) alert('Nip07 Login Failed');
else {
$currentUser = user;
+ $currentUser.fetchProfile();
localStorage.setItem('nostr-key-method', 'nip07');
localStorage.setItem('nostr-target-npub', $currentUser.npub);
+ dispatch('closeModal');
+ console.debug($currentUser);
}
}
@@ -26,7 +35,5 @@
{:else}
-
+
{/if}
diff --git a/src/lib/components/NavBar.svelte b/src/lib/components/NavBar.svelte
index 1c82a3b..7d30aa7 100644
--- a/src/lib/components/NavBar.svelte
+++ b/src/lib/components/NavBar.svelte
@@ -2,6 +2,7 @@
import {} from '@nostr-dev-kit/ndk-svelte-components';
import Avatar from '$lib/components/Avatar.svelte';
import { ndk } from '$lib/stores/nostr';
+ import { currentUser } from '$lib/store';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
@@ -10,7 +11,7 @@
let avatarimage: string | undefined;
$: {
- avatarimage = $ndk.activeUser?.profile?.image;
+ avatarimage = $currentUser?.profile?.image;
}
function signIn() {
@@ -44,11 +45,9 @@
diff --git a/src/lib/stores/nostr.ts b/src/lib/stores/nostr.ts
index af4fdcf..cca7dfc 100644
--- a/src/lib/stores/nostr.ts
+++ b/src/lib/stores/nostr.ts
@@ -20,13 +20,14 @@ if (!relayList || !Array.isArray(relayList) || relayList.length === 0) {
relayList = defaultRelays;
}
-const _ndk: NDKSvelte = new NDKSvelte({
+const _ndk: NDK = new NDK({
devWriteRelayUrls: ['wss://relay.strfront.com'],
explicitRelayUrls: relayList,
enableOutboxModel: true,
autoConnectUserRelays: true,
- autoFetchUserMutelist: true
-}) as NDKSvelte;
+ autoFetchUserMutelist: true,
+ clientName: 'cofabricate'
+}) as NDK;
_ndk.connect();
@@ -34,6 +35,7 @@ console.log(_ndk.activeUser?.profile);
const ndkStore = writable(_ndk);
+
export const ndk = ndkStore;
const _bunkerNDK = new NDK({
diff --git a/src/lib/stores/sesson.ts b/src/lib/stores/sesson.ts
new file mode 100644
index 0000000..322994c
--- /dev/null
+++ b/src/lib/stores/sesson.ts
@@ -0,0 +1,4 @@
+import { writable } from "svelte/store";
+
+export type LoginState = 'logging-in' | 'logged-in' | 'contacting-remote-signer' | 'logged-out';
+export const loginState = writable(null);
\ No newline at end of file
diff --git a/src/lib/utils/login.ts b/src/lib/utils/login.ts
index e622b4f..c0dd86e 100644
--- a/src/lib/utils/login.ts
+++ b/src/lib/utils/login.ts
@@ -1,50 +1,47 @@
import type NDK from '@nostr-dev-kit/ndk';
-import { NDKNip07Signer, NDKNip46Signer, NDKPrivateKeySigner, NDKUser } from '@nostr-dev-kit/ndk';
+import {
+ NDKNip07Signer,
+ NDKNip46Signer,
+ NDKPrivateKeySigner,
+ NDKUser,
+ type Hexpubkey,
+ type NDKSigner
+} from '@nostr-dev-kit/ndk';
+import { bunkerNDK, ndk } from '$lib/stores/nostr';
+import { currentUser } from '$lib/store';
+import { loginState } from '$lib/stores/sesson';
+import { get } from 'svelte/store';
export type LoginMethod = 'none' | 'pk' | 'nip07' | 'nip46';
+const $ndk = get(ndk);
+const $bunkerNDK = get(bunkerNDK);
-export async function login(
- ndk: NDK,
- bunkerNDK?: NDK,
- method?: LoginMethod
-): Promise {
- const nostrKeyMethod = method || localStorage.getItem('nostr-key-method');
+export async function login(method: LoginMethod, userPubkey?: string ): Promise {
+ console.debug(`logging in with ${method}`);
+ let u: NDKUser | null | undefined;
- switch (nostrKeyMethod) {
+ switch (method) {
case 'none':
+ loginState.set(null);
return null;
case 'pk':
const key = localStorage.getItem('nostr-key');
if (!key) return null;
-
- const signer = new NDKPrivateKeySigner(key);
- ndk.signer = signer;
- const user = await signer.user();
- if (user) user.ndk = ndk;
- return user;
+ else return await pkLogin(key);
case 'nip07':
- return nip07SignIn(ndk);
+ u = await nip07Login($ndk);
+ console.debug('Logged in as: ', u);
+ loginState.set('logged-in');
+ return u;
case 'nip46':
- const promise = new Promise((resolve, reject) => {
- const existingPrivateKey = localStorage.getItem('nostr-nsecbunker-key');
-
- if (!bunkerNDK) bunkerNDK = ndk;
-
- if (existingPrivateKey) {
- bunkerNDK.connect(2500);
- bunkerNDK.pool.on('relay:connect', async () => {
- const user = await nip46SignIn(ndk, bunkerNDK!, existingPrivateKey);
- resolve(user);
- });
- }
- });
+ return await nip46Login(userPubkey);
default: {
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve) => {
let loadAttempts = 0;
- const loadNip07Interval = setInterval(() => {
+ const loadNip07Interval = setInterval(async () => {
if (window.nostr) {
clearInterval(loadNip07Interval);
- const user = nip07SignIn(ndk);
+ const user = await nip07Login($ndk);
resolve(user);
}
if (loadAttempts++ > 10) clearInterval(loadNip07Interval);
@@ -55,51 +52,97 @@ export async function login(
}
}
-async function nip07SignIn(ndk: NDK): Promise {
- const storedNpub = localStorage.getItem('currentUserNpub');
+async function pkLogin(key: string): Promise {
+ const signer = new NDKPrivateKeySigner(key);
+ const u = await signer.user();
+ if (u) loggedIn(signer, u!, 'pk');
+ return u;
+}
+
+async function nip07Login(ndk: NDK): Promise {
+ const storedNpub = localStorage.getItem('pubkey');
let user: NDKUser | null = null;
if (storedNpub) {
user = new NDKUser({ npub: storedNpub });
user.ndk = ndk;
+ console.debug('Nip07 - logging in with stored npub', storedNpub);
}
if (window.nostr) {
try {
ndk.signer = new NDKNip07Signer();
- user = await ndk.signer.user();
+ user = await ndk.signer?.blockUntilReady();
+ ndk.activeUser = user;
user.ndk = ndk;
- localStorage.setItem('currentUserNpub', user.npub);
- ndk = ndk;
+ console.debug('Nip07 Login user:', user);
+ console.debug('NDK: ', ndk);
+ if (user) localStorage.setItem('nostr-key-method', 'nip07');
+ localStorage.setItem('pubkey', user.pubkey);
} catch (e) {}
}
-
+ if (user) await user.fetchProfile();
return user;
}
-async function nip46SignIn(
- ndk: NDK,
- bunkerNDK: NDK,
- existingPrivateKey: string
-): Promise {
- const npub = localStorage.getItem('nostr-target-npub')!;
- const remoteUser = new NDKUser({ npub });
- let user: NDKUser | null = null;
- remoteUser.ndk = bunkerNDK;
+async function nip46Login(remotePubkey?: Hexpubkey): Promise {
+ const existingPrivateKey = localStorage.getItem('nostr-nsecbunker-key')!;
+ let remoteUser: NDKUser | undefined;
- // check if there is a private key stored in localStorage
- let localSigner: NDKPrivateKeySigner | undefined = undefined;
+ console.debug({ existingPrivateKey, remotePubkey });
- if (existingPrivateKey) {
- localSigner = new NDKPrivateKeySigner(existingPrivateKey);
+ if (!existingPrivateKey) return null;
+
+ if (remotePubkey) remoteUser = $ndk.getUser({ pubkey: remotePubkey });
+
+ if (!remoteUser) return null;
+
+ currentUser.set(remoteUser);
+
+ console.debug('NIP46 setting user: ', remoteUser);
+
+ $bunkerNDK.pool.on('relay:ready', async () => {
+ console.debug('bunker relay ready');
+ loginState.set('contacting-remote-signer');
+ await nip46SignIn(existingPrivateKey, remoteUser!);
+ });
+
+ console.debug('connecting to nsecbunker relay');
+ $bunkerNDK.connect(2500);
+ return remoteUser;
+}
+
+async function nip46SignIn(existingPrivateKey: string, remoteUser: NDKUser) {
+ remoteUser.ndk = $bunkerNDK;
+ let localSigner: NDKPrivateKeySigner | null = null;
+
+ if (existingPrivateKey) localSigner = new NDKPrivateKeySigner(existingPrivateKey);
+ else {
+ alert('Local signer not available');
+ return null;
}
- const remoteSigner = new NDKNip46Signer(bunkerNDK, remoteUser.pubkey, localSigner);
+ const remoteSigner = new NDKNip46Signer($bunkerNDK, remoteUser.pubkey, localSigner!);
- await remoteSigner.blockUntilReady();
- ndk.signer = remoteSigner;
- user = remoteUser;
- user.ndk = ndk;
+ console.debug('contacting remote signer');
+ remoteSigner.blockUntilReady();
+ console.debug('Remote signer connected');
- return user;
+ localStorage.setItem('nostr-nsecbunker-key', localSigner.privateKey!);
+
+ loggedIn(remoteSigner, remoteUser, 'nip46');
+}
+
+export async function loggedIn(signer: NDKSigner, u: NDKUser, method: LoginMethod) {
+ const _ndk = get(ndk);
+ _ndk.signer = signer;
+ u.ndk = _ndk;
+ await u.fetchProfile();
+ currentUser.set(u);
+ _ndk.activeUser = u;
+ console.log('DEBUG setting user (loggedIn)', u);
+ loginState.set('logged-in');
+
+ localStorage.setItem('pubkey', u.pubkey);
+ localStorage.setItem('nostr-key-method', method);
}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 27792fd..81e1e2f 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,8 +1,8 @@
-
+
-
+
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 707eeb0..d5e982d 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,10 +1,5 @@
diff --git a/src/routes/browser-setup.ts b/src/routes/browser-setup.ts
new file mode 100644
index 0000000..8a90c9b
--- /dev/null
+++ b/src/routes/browser-setup.ts
@@ -0,0 +1,47 @@
+import { loginState } from "$lib/stores/sesson";
+import { loggedIn, login, type LoginMethod } from "$lib/utils/login";
+import { ndk } from "$lib/stores/nostr";
+import { get } from "svelte/store";
+import { currentUser } from "$lib/store";
+import { NDKNip07Signer, NDKUser } from "@nostr-dev-kit/ndk";
+
+const _ndk = get(ndk);
+
+export async function browserSetup() {
+ const pubkey = localStorage.getItem('pubkey');
+ console.debug(pubkey);
+
+ if (pubkey){
+ const u = _ndk.getUser({pubkey});
+ u.fetchProfile();
+ loginState.set('logging-in')
+ currentUser.set(u);
+ }
+
+ const method = localStorage.getItem('nostr-key-method') as LoginMethod;
+
+ if (!pubkey && method !== 'none') return newSessionTryNip07();
+
+ if (method && pubkey) {
+ return await login(method, pubkey);
+ }
+}
+
+export async function newSessionTryNip07() {
+ let signer: NDKNip07Signer | undefined;
+ let u: NDKUser | null | undefined;
+
+ try {
+ console.debug('trying nip07 signer');
+ signer = new NDKNip07Signer();
+ u = await signer.blockUntilReady();
+ u.fetchProfile();
+ } catch (e) {
+ console.debug('nip07Signer failed', e);
+ }
+
+ if (u && signer) {
+ console.debug('nip07Signer succeeded');
+ await loggedIn(signer, u, 'nip07');
+ }
+}
\ No newline at end of file