diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..21113f6
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,2 @@
+npm run lint
+npm run check
diff --git a/package-lock.json b/package-lock.json
index 02b6973..54df626 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"@tiptap/extension-placeholder": "^2.9.1",
"@tiptap/extension-text": "^2.6.6",
"@tiptap/suggestion": "^2.6.4",
+ "@types/qrcode": "^1.5.5",
"@types/throttle-debounce": "^5.0.2",
"@vite-pwa/assets-generator": "^0.2.6",
"@vite-pwa/sveltekit": "^0.6.6",
@@ -43,6 +44,7 @@
"dotenv": "^16.4.5",
"emoji-picker-element": "^1.22.8",
"fuse.js": "^7.0.0",
+ "husky": "^9.1.6",
"idb": "^8.0.0",
"nostr-editor": "^0.0.3",
"nostr-tools": "^2.7.2",
@@ -3764,6 +3766,15 @@
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
"dev": true
},
+ "node_modules/@types/qrcode": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz",
+ "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -6555,6 +6566,21 @@
"node": ">= 0.4"
}
},
+ "node_modules/husky": {
+ "version": "9.1.6",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz",
+ "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==",
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
"node_modules/ico-endec": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/ico-endec/-/ico-endec-0.1.6.tgz",
diff --git a/package.json b/package.json
index 2c8d306..f5c4e28 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
- "format": "prettier --write ."
+ "format": "prettier --write .",
+ "prepare": "husky"
},
"devDependencies": {
"@sveltejs/kit": "^2.0.0",
@@ -51,6 +52,7 @@
"@tiptap/extension-placeholder": "^2.9.1",
"@tiptap/extension-text": "^2.6.6",
"@tiptap/suggestion": "^2.6.4",
+ "@types/qrcode": "^1.5.5",
"@types/throttle-debounce": "^5.0.2",
"@vite-pwa/assets-generator": "^0.2.6",
"@vite-pwa/sveltekit": "^0.6.6",
@@ -68,6 +70,7 @@
"dotenv": "^16.4.5",
"emoji-picker-element": "^1.22.8",
"fuse.js": "^7.0.0",
+ "husky": "^9.1.6",
"idb": "^8.0.0",
"nostr-editor": "^0.0.3",
"nostr-tools": "^2.7.2",
diff --git a/src/app/commands.ts b/src/app/commands.ts
index d870f43..01108b3 100644
--- a/src/app/commands.ts
+++ b/src/app/commands.ts
@@ -30,7 +30,7 @@ import {
signer,
repository,
publishThunk,
- mergeThunks,
+ publishThunks,
loadProfile,
loadInboxRelaySelections,
profilesByPubkey,
@@ -335,15 +335,13 @@ export const sendWrapped = async ({
}) => {
const nip59 = Nip59.fromSigner(signer.get()!)
- return mergeThunks(
+ return publishThunks(
await Promise.all(
- uniq(pubkeys).map(async recipient =>
- publishThunk({
- event: await nip59.wrap(recipient, stamp(template)),
- relays: ctx.app.router.PubkeyInbox(recipient).getUrls(),
- delay,
- }),
- ),
+ uniq(pubkeys).map(async recipient => ({
+ event: await nip59.wrap(recipient, stamp(template)),
+ relays: ctx.app.router.PubkeyInbox(recipient).getUrls(),
+ delay,
+ })),
),
)
}
diff --git a/src/app/components/ChannelConversation.svelte b/src/app/components/ChannelConversation.svelte
index d271443..2b0eb7d 100644
--- a/src/app/components/ChannelConversation.svelte
+++ b/src/app/components/ChannelConversation.svelte
@@ -1,11 +1,11 @@
diff --git a/src/app/components/ChannelMessage.svelte b/src/app/components/ChannelMessage.svelte
index 9395c43..48241d5 100644
--- a/src/app/components/ChannelMessage.svelte
+++ b/src/app/components/ChannelMessage.svelte
@@ -2,8 +2,13 @@
import {readable} from "svelte/store"
import {hash} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
- import {deriveProfile, deriveProfileDisplay, formatTimestampAsTime, pubkey} from "@welshman/app"
- import type {Thunk} from "@welshman/app"
+ import {
+ thunks,
+ deriveProfile,
+ deriveProfileDisplay,
+ formatTimestampAsTime,
+ pubkey,
+ } from "@welshman/app"
import {isMobile} from "@lib/html"
import LongPress from "@lib/components/LongPress.svelte"
import Avatar from "@lib/components/Avatar.svelte"
@@ -16,13 +21,12 @@
import ChannelMessageEmojiButton from "@app/components/ChannelMessageEmojiButton.svelte"
import ChannelMessageMenuButton from "@app/components/ChannelMessageMenuButton.svelte"
import ChannelMessageMenuMobile from "@app/components/ChannelMessageMenuMobile.svelte"
- import {colors, thunks, tagRoom, deriveEvent, pubkeyLink} from "@app/state"
+ import {colors, tagRoom, deriveEvent, pubkeyLink} from "@app/state"
import {publishDelete, publishReaction} from "@app/commands"
import {pushDrawer, pushModal} from "@app/modal"
export let url, room
export let event: TrustedEvent
- export let thunk: Thunk
export let showPubkey = false
export let isHead = false
export let inert = false
@@ -90,7 +94,7 @@
{#if thunk}
-
+
{/if}
diff --git a/src/app/components/Chat.svelte b/src/app/components/Chat.svelte
index fa929ab..9a7127b 100644
--- a/src/app/components/Chat.svelte
+++ b/src/app/components/Chat.svelte
@@ -10,7 +10,7 @@
-
- {#if canCancel || isPending}
-
+
+ {#if isFailure && failure}
+ {@const [url, {message, status}] = failure}
+
+
+
+ Failed to send!
+
+
+ {:else if canCancel || isPending}
+
Sending...
{#if canCancel}
{/if}
- {:else if isFailure && failure}
- {@const [url, {message, status}] = failure}
-
-
-
- Failed to send!
-
-
{/if}
diff --git a/src/app/state.ts b/src/app/state.ts
index 42c405e..1938ab6 100644
--- a/src/app/state.ts
+++ b/src/app/state.ts
@@ -1,5 +1,5 @@
import twColors from "tailwindcss/colors"
-import {get, derived, writable} from "svelte/store"
+import {get, derived} from "svelte/store"
import {nip19} from "nostr-tools"
import type {Maybe} from "@welshman/lib"
import {
@@ -55,7 +55,7 @@ import {
userFollows,
ensurePlaintext,
} from "@welshman/app"
-import type {AppSyncOpts, Thunk} from "@welshman/app"
+import type {AppSyncOpts} from "@welshman/app"
import type {SubscribeRequestWithHandlers} from "@welshman/net"
import {deriveEvents, deriveEventsMapped, withGetter} from "@welshman/store"
@@ -144,8 +144,6 @@ export const pubkeyLink = (
relays = ctx.app.router.FromPubkeys([pubkey]).getUrls(),
) => entityLink(nip19.nprofileEncode({pubkey, relays}))
-export const thunks = writable({} as Record)
-
export const tagRoom = (room: string, url: string) => [ROOM, room, url]
export const getDefaultPubkeys = () => {
diff --git a/src/lib/components/LongPress.svelte b/src/lib/components/LongPress.svelte
index 83f60b8..f626b0b 100644
--- a/src/lib/components/LongPress.svelte
+++ b/src/lib/components/LongPress.svelte
@@ -21,7 +21,7 @@
const onTouchEnd = () => clearTimeout(timeout)
let touch: Touch
- let timeout: number
+ let timeout: any
{
return result
}
-type ScrollerOpts = {
+export type ScrollerOpts = {
onScroll: () => any
element: Element
threshold?: number
@@ -24,6 +24,11 @@ type ScrollerOpts = {
delay?: number
}
+export type Scroller = {
+ check: () => Promise
+ stop: () => void
+}
+
export const createScroller = ({
onScroll,
element,
diff --git a/src/routes/spaces/[relay]/[room]/+page.svelte b/src/routes/spaces/[relay]/[room]/+page.svelte
index be6f6a7..028eee5 100644
--- a/src/routes/spaces/[relay]/[room]/+page.svelte
+++ b/src/routes/spaces/[relay]/[room]/+page.svelte
@@ -10,7 +10,7 @@