Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e832af3e4 | |||
| 84b8650fa4 | |||
| 83abb5aa94 | |||
| a12eddb47b | |||
| c87166247c | |||
| 037c8cb41b | |||
| 79de2e1176 | |||
| d4b026a3ad | |||
| 00f383ff2e | |||
| 6f6bb508db | |||
| e2a0672ca5 | |||
| e2a5fe7a79 | |||
| 5d02ae75dc | |||
| 2460bbbc83 | |||
| 084d8d931b | |||
| 6ee4ac1a89 | |||
| 1d07097350 | |||
| 63d6b362c7 | |||
| bfed277ea9 | |||
| 9e8aa2ef3a | |||
| 4bbc0878f7 |
+1
-1
@@ -13,6 +13,6 @@ VITE_INDEXER_RELAYS=wss://purplepag.es/,wss://relay.damus.io/,wss://relay.nostr.
|
||||
VITE_SIGNER_RELAYS=wss://relay.nsec.app/,wss://bucket.coracle.social/
|
||||
VITE_NOTIFIER_PUBKEY=27b7c2ed89ef78322114225ea3ebf5f72c7767c2528d4d0c1854d039c00085df
|
||||
VITE_NOTIFIER_RELAY=wss://anchor.coracle.social/
|
||||
VITE_VAPID_PUBLIC_KEY=
|
||||
VITE_VAPID_PUBLIC_KEY=BIt2D4BdgdbCowD_0d3Np6GbrIGHxd7aIEUeZNe3hQuRlHz02OhzvDaai0XSFoJYVzSzdMjdyW-QhvW9_yq8j4Y
|
||||
VITE_GLITCHTIP_API_KEY=
|
||||
GLITCHTIP_AUTH_TOKEN=
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
# 1.2.2
|
||||
|
||||
* Fix phantom chat notifications
|
||||
* Fix zaps on mobile
|
||||
|
||||
# 1.2.1
|
||||
|
||||
* Add zaps to chat, threads, and events
|
||||
* Add funding goals
|
||||
* Add NWC support
|
||||
* Add wallet settings page
|
||||
* Handle invalid bunker url
|
||||
* Fix sidebar overflow
|
||||
* Fix profile npub display
|
||||
|
||||
# 1.2.0
|
||||
|
||||
* Fix sort order of thread comments
|
||||
|
||||
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "social.flotilla"
|
||||
minSdk rootProject.ext.minSdkVersion
|
||||
targetSdk rootProject.ext.targetSdkVersion
|
||||
versionCode 21
|
||||
versionName "1.2.0"
|
||||
versionCode 23
|
||||
versionName "1.2.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Fetch tags and set to env vars
|
||||
git fetch --prune --unshallow --tags
|
||||
git describe --tags --abbrev=0
|
||||
git fetch --prune --unshallow --tags || true
|
||||
git describe --tags --abbrev=0 || true
|
||||
export VITE_BUILD_VERSION=$RENDER_GIT_COMMIT
|
||||
export VITE_BUILD_HASH=$RENDER_GIT_COMMIT
|
||||
|
||||
|
||||
@@ -354,14 +354,14 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 13;
|
||||
CURRENT_PROJECT_VERSION = 16;
|
||||
DEVELOPMENT_TEAM = S26U9DYW3A;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.2.2;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -380,14 +380,14 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 13;
|
||||
CURRENT_PROJECT_VERSION = 16;
|
||||
DEVELOPMENT_TEAM = S26U9DYW3A;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.2.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
+16
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "flotilla",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -47,24 +47,26 @@
|
||||
"@capacitor/keyboard": "^7.0.0",
|
||||
"@capacitor/push-notifications": "^7.0.1",
|
||||
"@capawesome/capacitor-badge": "^7.0.1",
|
||||
"@getalby/sdk": "^5.1.0",
|
||||
"@poppanator/sveltekit-svg": "^4.2.1",
|
||||
"@sentry/browser": "^8.35.0",
|
||||
"@sveltejs/adapter-static": "^3.0.4",
|
||||
"@tiptap/core": "^2.12.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@vite-pwa/assets-generator": "^0.2.6",
|
||||
"@vite-pwa/sveltekit": "^0.6.6",
|
||||
"@welshman/app": "^0.3.8",
|
||||
"@welshman/content": "^0.3.8",
|
||||
"@welshman/editor": "^0.3.8",
|
||||
"@welshman/feeds": "^0.3.8",
|
||||
"@welshman/lib": "^0.3.8",
|
||||
"@welshman/net": "^0.3.8",
|
||||
"@welshman/relay": "^0.3.8",
|
||||
"@welshman/router": "^0.3.8",
|
||||
"@welshman/signer": "^0.3.8",
|
||||
"@welshman/store": "^0.3.8",
|
||||
"@welshman/util": "^0.3.8",
|
||||
"@welshman/app": "^0.4.0",
|
||||
"@welshman/content": "^0.4.0",
|
||||
"@welshman/editor": "^0.4.0",
|
||||
"@welshman/feeds": "^0.4.0",
|
||||
"@welshman/lib": "^0.4.0",
|
||||
"@welshman/net": "^0.4.0",
|
||||
"@welshman/relay": "^0.4.0",
|
||||
"@welshman/router": "^0.4.0",
|
||||
"@welshman/signer": "^0.4.0",
|
||||
"@welshman/store": "^0.4.0",
|
||||
"@welshman/util": "^0.4.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"daisyui": "^4.12.10",
|
||||
"date-picker-svelte": "^2.13.0",
|
||||
@@ -76,7 +78,9 @@
|
||||
"nostr-signer-capacitor-plugin": "^0.0.4",
|
||||
"nostr-tools": "^2.14.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"throttle-debounce": "^5.0.2",
|
||||
"tippy.js": "^6.3.7"
|
||||
},
|
||||
"pnpm": {
|
||||
|
||||
Generated
+201
-140
@@ -35,6 +35,9 @@ importers:
|
||||
'@capawesome/capacitor-badge':
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1(@capacitor/core@7.2.0)
|
||||
'@getalby/sdk':
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0(typescript@5.8.3)
|
||||
'@poppanator/sveltekit-svg':
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(rollup@2.79.2)(svelte@5.25.10)(svgo@3.3.2)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0))
|
||||
@@ -50,6 +53,9 @@ importers:
|
||||
'@types/qrcode':
|
||||
specifier: ^1.5.5
|
||||
version: 1.5.5
|
||||
'@types/throttle-debounce':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
'@vite-pwa/assets-generator':
|
||||
specifier: ^0.2.6
|
||||
version: 0.2.6
|
||||
@@ -57,38 +63,38 @@ importers:
|
||||
specifier: ^0.6.6
|
||||
version: 0.6.8(@sveltejs/kit@2.20.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.25.10)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0)))(svelte@5.25.10)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0)))(@vite-pwa/assets-generator@0.2.6)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@0.2.6)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
|
||||
'@welshman/app':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/content':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(typescript@5.8.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(typescript@5.8.3)
|
||||
'@welshman/editor':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(@tiptap/extension-image@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)
|
||||
'@welshman/feeds':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/lib':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
'@welshman/net':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(typescript@5.8.3)(ws@8.18.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/relay':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(typescript@5.8.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(typescript@5.8.3)
|
||||
'@welshman/router':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(typescript@5.8.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(typescript@5.8.3)
|
||||
'@welshman/signer':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/store':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(typescript@5.8.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(typescript@5.8.3)
|
||||
'@welshman/util':
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.8(typescript@5.8.3)
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(typescript@5.8.3)
|
||||
compressorjs:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
@@ -122,9 +128,15 @@ importers:
|
||||
prettier-plugin-tailwindcss:
|
||||
specifier: ^0.6.5
|
||||
version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.25.10))(prettier@3.5.3)
|
||||
qr-scanner:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2
|
||||
qrcode:
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4
|
||||
throttle-debounce:
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
tippy.js:
|
||||
specifier: ^6.3.7
|
||||
version: 6.3.7
|
||||
@@ -944,6 +956,14 @@ packages:
|
||||
resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@getalby/lightning-tools@5.2.0':
|
||||
resolution: {integrity: sha512-8kBvENBTMh541VjGKhw3I29+549/C02gLSh3AQaMfoMNSZaMxfQW+7dcMcc7vbFaCKEcEe18ST5bUveTRBuXCQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@getalby/sdk@5.1.0':
|
||||
resolution: {integrity: sha512-0ijo4enzoxZinyhOMFlR4h3qTQ9I0Se+dBkefk0ja5zOcpi61ZqT86n0T+7u94l8SH6/poysFBObdtN61u+6tQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@@ -1383,77 +1403,77 @@ packages:
|
||||
peerDependencies:
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-code-block@2.23.0':
|
||||
resolution: {integrity: sha512-p8iizp5nQBBhYPrIgBVwEqcDnc2fFRAZCXy/xjmAk2kKOhB7NYe3+1yrbFcQKVAdaUFxG+BRj2WxNDeeRt5tJA==}
|
||||
'@tiptap/extension-code-block@2.26.1':
|
||||
resolution: {integrity: sha512-/TDDOwONl0qEUc4+B6V9NnWtSjz95eg7/8uCb8Y8iRbGvI9vT4/znRKofFxstvKmW4URu/H74/g0ywV57h0B+A==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-code@2.23.0':
|
||||
resolution: {integrity: sha512-Ip/5+kNoqrxYPHLnZMf7i6wfjjRuR5QgfC3IR3Mk1WQM1JGXCLL+uUjTUxKXFUj28hjSJfsmVbTUhoVvgZEWfw==}
|
||||
'@tiptap/extension-code@2.26.1':
|
||||
resolution: {integrity: sha512-GU9deB1A/Tr4FMPu71CvlcjGKwRhGYz60wQ8m4aM+ELZcVIcZRa1ebR8bExRIEWnvRztQuyRiCQzw2N0xQJ1QQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-document@2.23.0':
|
||||
resolution: {integrity: sha512-kuRPqH0UdjZ4RcnpPELsu1N8LqeixEin+mv5eaQJI/aZ6rFq+kcY4UZF3C7q56Rat5r9CgHBiZbD0t5l6E3gdA==}
|
||||
'@tiptap/extension-document@2.26.1':
|
||||
resolution: {integrity: sha512-2P2IZp1NRAE+21mRuFBiP3X2WKfZ6kUC23NJKpn8bcOamY3obYqCt0ltGPhE4eR8n8QAl2fI/3jIgjR07dC8ow==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-dropcursor@2.23.0':
|
||||
resolution: {integrity: sha512-m2LzkJpipHLPEllD3MXZQMssu7Xng7YJOJ8ZNDkF0uUkXljwh7G0ROjGNKUlV8/dqoCVmJIZIyF6t9saQwTTbA==}
|
||||
'@tiptap/extension-dropcursor@2.26.1':
|
||||
resolution: {integrity: sha512-JkDQU2ZYFOuT5mNYb8OiWGwD1HcjbtmX8tLNugQbToECmz9WvVPqJmn7V/q8VGpP81iEECz/IsyRmuf2kSD4uA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-gapcursor@2.23.0':
|
||||
resolution: {integrity: sha512-SpYsDtMiVwqcSB84g714PrnHo985R5UiIaGngef6iMNy/0xjKcO0tj/feu0WwJDuSj22Opzlnb/Ld/D4Va27Ng==}
|
||||
'@tiptap/extension-gapcursor@2.26.1':
|
||||
resolution: {integrity: sha512-KOiMZc3PwJS3hR0nSq5d0TJi2jkNZkLZElcT6pCEnhRHzPH6dRMu9GM5Jj798ZRUy0T9UFcKJalFZaDxnmRnpg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-hard-break@2.23.0':
|
||||
resolution: {integrity: sha512-OpNBEYv9HDUPo8SgvmI5oPd0b+xmdadtFyL7t4lxhYar8n5NDYubaXYgbKcdJfXvUxEeGwdc3ePnTFpsF0mrYw==}
|
||||
'@tiptap/extension-hard-break@2.26.1':
|
||||
resolution: {integrity: sha512-d6uStdNKi8kjPlHAyO59M6KGWATNwhLCD7dng0NXfwGndc22fthzIk/6j9F6ltQx30huy5qQram6j3JXwNACoA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-history@2.23.0':
|
||||
resolution: {integrity: sha512-W+2bZ/02nm56g/wmEaSx9QcdZ8mHjoFyc8MKf54Mrzi+nIdNjsNreKrn1yCp683CGEPd8DLadDFkz0o13N+rxA==}
|
||||
'@tiptap/extension-history@2.26.1':
|
||||
resolution: {integrity: sha512-m6YR1gkkauIDo3PRl0gP+7Oc4n5OqDzcjVh6LvWREmZP8nmi94hfseYbqOXUb6RPHIc0JKF02eiRifT4MSd2nw==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-image@2.23.0':
|
||||
resolution: {integrity: sha512-/rW2+a21VBGBv5c/78CVW8XA7bThSqE3FqcBtWyq8IxZoe8Hj9+Jac7FcB2YR3aY0BeHwso474e1RuVr1iYBKQ==}
|
||||
'@tiptap/extension-image@2.26.1':
|
||||
resolution: {integrity: sha512-96+MaYBJebQlR/ik5W72GLUfXdEoxFs+6jsoERxbM5qEdhb7TEnodBFtWZOwgDO27kFd6rSNZuW9r5KJNtljEg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-link@2.23.0':
|
||||
resolution: {integrity: sha512-D+ethAE8+2f7RH7kqS+//EsC2wNblhmssJYVE0hCXM5BKIBixjs8eCOAvLbJsw0u/5LqFYjsyAimTqa4hD5uvg==}
|
||||
'@tiptap/extension-link@2.26.1':
|
||||
resolution: {integrity: sha512-7yfum5Jymkue/uOSTQPt2SmkZIdZx7t3QhZLqBU7R9ettkdSCBgEGok6N+scJM1R1Zes+maSckLm0JZw5BKYNA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-paragraph@2.23.0':
|
||||
resolution: {integrity: sha512-MXhRkb741UOcJp2evG/H0MY3WJQnX7z8PsejmJbJXOHBrS/Esxq0AlrDAjuFhbfAnJwYiWQ1lk6ucvKV6DhFuQ==}
|
||||
'@tiptap/extension-paragraph@2.26.1':
|
||||
resolution: {integrity: sha512-UezvM9VDRAVJlX1tykgHWSD1g3MKfVMWWZ+Tg+PE4+kizOwoYkRWznVPgCAxjmyHajxpCKRXgqTZkOxjJ9Kjzg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-placeholder@2.23.0':
|
||||
resolution: {integrity: sha512-I5RQk0qn6nj7l7z4mWKIxjO2nluvKsm00W2CbC75b4YcScBfsMInHQdjN2s+W8xuF0zquhwVITxA+Bmn4zynqg==}
|
||||
'@tiptap/extension-placeholder@2.26.1':
|
||||
resolution: {integrity: sha512-MBlqbkd+63btY7Qu+SqrXvWjPwooGZDsLTtl7jp52BczBl61cq9yygglt9XpM11TFMBdySgdLHBrLtQ0B7fBlw==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-text@2.23.0':
|
||||
resolution: {integrity: sha512-hF+CU1H4B4UgqjBXXPPaACVZdSGuMH0TDYTd7h403qUAIBKkYbjuan7laQpiT0qnF0Dg+sGgvmGcd4H1tTBM8g==}
|
||||
'@tiptap/extension-text@2.26.1':
|
||||
resolution: {integrity: sha512-p2n8WVMd/2vckdJlol24acaTDIZAhI7qle5cM75bn01sOEZoFlSw6SwINOULrUCzNJsYb43qrLEibZb4j2LeQw==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/pm@2.12.0':
|
||||
resolution: {integrity: sha512-TNzVwpeNzFfHAcYTOKqX9iU4fRxliyoZrCnERR+RRzeg7gWrXrCLubQt1WEx0sojMAfznshSL3M5HGsYjEbYwA==}
|
||||
|
||||
'@tiptap/suggestion@2.23.0':
|
||||
resolution: {integrity: sha512-WUUGADu8ZezXZ4hXZWdfGcfoitB5tiBrc2u1oXqqL8VmJJedhY4MdWUPYqgh3359tAI2yJWmv+gPabX361gBEA==}
|
||||
'@tiptap/suggestion@2.26.1':
|
||||
resolution: {integrity: sha512-iNWJdQN7h01keNoVwyCsdI7ZX11YkrexZjCnutWK17Dd72s3NYVTmQXu7saftwddT4nDdlczNxAFosrt0zMhcg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
@@ -1528,6 +1548,9 @@ packages:
|
||||
'@types/normalize-package-data@2.4.4':
|
||||
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
||||
|
||||
'@types/offscreencanvas@2019.7.3':
|
||||
resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==}
|
||||
|
||||
'@types/qrcode@1.5.5':
|
||||
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
|
||||
|
||||
@@ -1609,41 +1632,41 @@ packages:
|
||||
'@vite-pwa/assets-generator':
|
||||
optional: true
|
||||
|
||||
'@welshman/app@0.3.8':
|
||||
resolution: {integrity: sha512-WmvqB8Z/qPN0dJerf3QTp5MniZQGXToNXJWorEFA3LQwBJWAovPFfdq8xoNGe9tWwYl0m9Rt/ObZf3gFlX28Cw==}
|
||||
'@welshman/app@0.4.0':
|
||||
resolution: {integrity: sha512-LTlqbuiRFYAdwXIUYPOxaAusjhlj2ZgZlAuyEpQoBwNTyD7TUaTXj0kA5pbQZLFXWYuqDmrDB14Nl1zzBJBESQ==}
|
||||
|
||||
'@welshman/content@0.3.8':
|
||||
resolution: {integrity: sha512-ic7imQR0cpolUlwnWVfUqiIo9zkOt6DS2M92BD4Y/mCLGrUMzlUw0/NE5TzBJ6dSywVh8/aBBOTWotzpmbttKg==}
|
||||
'@welshman/content@0.4.0':
|
||||
resolution: {integrity: sha512-3pWxr0Byc/Asmvlnq5UchkT0yeaGg63xTEk9fVJyzIrphIxn5bboaIixEw7y2w2lggFaqHgx+DFrulmhdJ9dXQ==}
|
||||
|
||||
'@welshman/editor@0.3.8':
|
||||
resolution: {integrity: sha512-XZ/cXEM3MIwhR7CZvboH2askm9dZJ9cH7/CS4Asd3Q/OaaPUrCTCoacEpR1z3raMMOz0TiDn+BgjGqrHYU/58Q==}
|
||||
'@welshman/editor@0.4.0':
|
||||
resolution: {integrity: sha512-aIt/t+pMs2XKWZ6wN58jdPWlN9MXVdK1rccKk6Z54ckarCzB4B7usSZvstwMMkmZra/HPLOaWw5KXqhDR1YiUA==}
|
||||
|
||||
'@welshman/feeds@0.3.8':
|
||||
resolution: {integrity: sha512-Rjf36Eng22PMY9p1yepMDfXH2dlCPg5yBVidu8WArFWLgxSv67DCxAsDlHkRd/mJD5DARFDob1WZP9bUsljJBw==}
|
||||
'@welshman/feeds@0.4.0':
|
||||
resolution: {integrity: sha512-fwQ4eDzEtcSxFj2LKps6XYFXuZv6lFXKDTq+Nvs5tNYYJUbv/Cz4x3aLQo2ivInz9gAMOLmgpIgNCxkzMqCnoQ==}
|
||||
|
||||
'@welshman/lib@0.3.8':
|
||||
resolution: {integrity: sha512-fbq94UkyoC7kieAlWsDzH6zgZt+GVhkh1RiFvtE4iGug3bIPxh1QmmvO70EHlFkiEDcYAFEEHd9OMm6/JRc97Q==}
|
||||
'@welshman/lib@0.4.0':
|
||||
resolution: {integrity: sha512-1GPQ2X1FT2R55KWPKhDs+ZK/EkVpeMkVwWdSXC88w+YCoUop00keFm7P452kQKgA/lixNURJSyeWgfI2tUdpkQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
'@welshman/net@0.3.8':
|
||||
resolution: {integrity: sha512-DWNL+BGmOGCfXdYOnpd3IJ7IuCskftUfXnjBo8F0rkHWUWAE6Q1zTJmdJG3kDVDoWl3X4XBfxkl4WGckF9pSkw==}
|
||||
'@welshman/net@0.4.0':
|
||||
resolution: {integrity: sha512-QBU5dsALCr9V51lIyNseUDIfvjCJo6VFWe6G1gkJ1PQGh5rgJNZJWCaD430PDpCKsufv2JIkCVYZG5xYZgxzMg==}
|
||||
|
||||
'@welshman/relay@0.3.8':
|
||||
resolution: {integrity: sha512-vWUOxvG4WV0+EsC/BYoCF9L2W/qml4TIAnGHmWpDt9jTS82kIQt3cu2+3uJs9IZli+etRJ3xYJLVvfzfQqBaig==}
|
||||
'@welshman/relay@0.4.0':
|
||||
resolution: {integrity: sha512-5zTPSDPhMR2v55hotQf4JO3XgBXEws4k/xChAbYZDfUwxG7HQxmDM2n56aFrKqgI3w+qp8l1lx/1KeksKvBiWw==}
|
||||
|
||||
'@welshman/router@0.3.8':
|
||||
resolution: {integrity: sha512-3Gn3yjMbQ9sQ8qsX5bjUtTSKkwdOREqGVzcQiBq1FRATh42ih06Opu5/t8ujcMRMhV3w/02ckfZJ46DF34ozWQ==}
|
||||
'@welshman/router@0.4.0':
|
||||
resolution: {integrity: sha512-ccpx9QrJ7Uq3CI7r/PyBOwO0G/2xknKXN0xLW995hta1Z1bUzYWhz+C9YvoceDPHCUPaZotBCgdmpY9oOiYqHg==}
|
||||
|
||||
'@welshman/signer@0.3.8':
|
||||
resolution: {integrity: sha512-L3hAJlS0smS4uSC5b1xskHDbrkuCykXr5b6Z9aNV9MJjczbb5pCuYjHf9lCvcmqaz4NN4kcksXlLZgxMbE/5Jg==}
|
||||
'@welshman/signer@0.4.0':
|
||||
resolution: {integrity: sha512-I+4l1gmSBVQkFtu6Bm5aAxsFXlE5oXeCsUX+GSsTb0Pg1e4FnTMgeaI3xM8tcCLma4EK+3mD7Yi9MaZMSYX8YQ==}
|
||||
peerDependencies:
|
||||
nostr-signer-capacitor-plugin: ~0.0.4
|
||||
|
||||
'@welshman/store@0.3.8':
|
||||
resolution: {integrity: sha512-SHKF9RjcvoqcduHDleKSjhubrS10f5XmcU2kBPFy/U9G7hi4M5f4HQa46Kt7Mf4OHuofnjRw+4RLopkqjFL3ow==}
|
||||
'@welshman/store@0.4.0':
|
||||
resolution: {integrity: sha512-/gIX1hTTGPkhFlMm91oY7khqriIZwnNIFs3leWIbJGWXLd4pd4fMFM7bKuOTuEsnHkB9thkeXurAdYMquhlHOw==}
|
||||
|
||||
'@welshman/util@0.3.8':
|
||||
resolution: {integrity: sha512-+doowqtIjUChPGdmGNopO6bAvC/0LkF2zKaEKPHnIfDAsw4gAJyuAvIlS/PUv8tY6scXORGeGMnoBC0Htd3Ovg==}
|
||||
'@welshman/util@0.4.0':
|
||||
resolution: {integrity: sha512-UiJyqXeWEx0s83M0AD/bN5ylvpCfYUSjLepb0QxxZpBPnZgMj07oO1OS4967QFuePSvmjTQNcxy3ABm8aagxGg==}
|
||||
|
||||
'@xml-tools/parser@1.0.11':
|
||||
resolution: {integrity: sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==}
|
||||
@@ -3412,6 +3435,14 @@ packages:
|
||||
peerDependencies:
|
||||
'@capacitor/core': ^7.0.0
|
||||
|
||||
nostr-tools@2.12.0:
|
||||
resolution: {integrity: sha512-pUWEb020gTvt1XZvTa8AKNIHWFapjsv2NKyk43Ez2nnvz6WSXsrTFE0XtkNLSRBjPn6EpxumKeNiVzLz74jNSA==}
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
nostr-tools@2.14.2:
|
||||
resolution: {integrity: sha512-YOIOn5EdJ2Kq5sQW5Zh4wOcqzR6kUyrCDHG4+mVD2szzthsyOTpiWX0yrwaRZGlHJG6q83vkhg95qc2W201XTQ==}
|
||||
peerDependencies:
|
||||
@@ -3835,6 +3866,9 @@ packages:
|
||||
|
||||
(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
|
||||
|
||||
qr-scanner@1.4.2:
|
||||
resolution: {integrity: sha512-kV1yQUe2FENvn59tMZW6mOVfpq9mGxGf8l6+EGaXUOd4RBOLg7tRC83OrirM5AtDvZRpdjdlXURsHreAOSPOUw==}
|
||||
|
||||
qrcode@1.5.4:
|
||||
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@@ -5689,6 +5723,15 @@ snapshots:
|
||||
'@eslint/core': 0.13.0
|
||||
levn: 0.4.1
|
||||
|
||||
'@getalby/lightning-tools@5.2.0': {}
|
||||
|
||||
'@getalby/sdk@5.1.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@getalby/lightning-tools': 5.2.0
|
||||
nostr-tools: 2.12.0(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
@@ -6169,58 +6212,58 @@ snapshots:
|
||||
dependencies:
|
||||
'@tiptap/pm': 2.12.0
|
||||
|
||||
'@tiptap/extension-code-block@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/extension-code-block@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
|
||||
'@tiptap/extension-code@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
'@tiptap/extension-code@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
|
||||
'@tiptap/extension-document@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
'@tiptap/extension-document@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
|
||||
'@tiptap/extension-dropcursor@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/extension-dropcursor@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
|
||||
'@tiptap/extension-gapcursor@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/extension-gapcursor@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
|
||||
'@tiptap/extension-hard-break@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
'@tiptap/extension-hard-break@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
|
||||
'@tiptap/extension-history@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/extension-history@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
|
||||
'@tiptap/extension-image@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
'@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
|
||||
'@tiptap/extension-link@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
linkifyjs: 4.3.1
|
||||
|
||||
'@tiptap/extension-paragraph@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
'@tiptap/extension-paragraph@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
|
||||
'@tiptap/extension-placeholder@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/extension-placeholder@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
|
||||
'@tiptap/extension-text@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
'@tiptap/extension-text@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
|
||||
@@ -6245,7 +6288,7 @@ snapshots:
|
||||
prosemirror-transform: 1.10.4
|
||||
prosemirror-view: 1.39.3
|
||||
|
||||
'@tiptap/suggestion@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
'@tiptap/suggestion@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
@@ -6341,6 +6384,8 @@ snapshots:
|
||||
|
||||
'@types/normalize-package-data@2.4.4': {}
|
||||
|
||||
'@types/offscreencanvas@2019.7.3': {}
|
||||
|
||||
'@types/qrcode@1.5.5':
|
||||
dependencies:
|
||||
'@types/node': 22.14.0
|
||||
@@ -6454,17 +6499,17 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@vite-pwa/assets-generator': 0.2.6
|
||||
|
||||
'@welshman/app@0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
|
||||
'@welshman/app@0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
|
||||
dependencies:
|
||||
'@types/throttle-debounce': 5.0.2
|
||||
'@welshman/feeds': 0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/net': 0.3.8(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/relay': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/router': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/signer': 0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/store': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/feeds': 0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/net': 0.4.0(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/relay': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/router': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/signer': 0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/store': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
fuse.js: 7.1.0
|
||||
idb: 8.0.2
|
||||
svelte: 4.2.20
|
||||
@@ -6474,31 +6519,31 @@ snapshots:
|
||||
- typescript
|
||||
- ws
|
||||
|
||||
'@welshman/content@0.3.8(typescript@5.8.3)':
|
||||
'@welshman/content@0.4.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@braintree/sanitize-url': 7.1.1
|
||||
nostr-tools: 2.14.2(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@welshman/editor@0.3.8(@tiptap/extension-image@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)':
|
||||
'@welshman/editor@0.4.0(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-code': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-code-block': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-document': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-dropcursor': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-gapcursor': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-hard-break': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-history': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-paragraph': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-placeholder': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-text': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-code': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-code-block': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-document': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-dropcursor': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-gapcursor': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-hard-break': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-history': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-paragraph': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-placeholder': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-text': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/pm': 2.12.0
|
||||
'@tiptap/suggestion': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
nostr-editor: 1.0.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.1)(nostr-tools@2.14.2(typescript@5.8.3))(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))
|
||||
'@tiptap/suggestion': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
nostr-editor: 1.0.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.1)(nostr-tools@2.14.2(typescript@5.8.3))(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))
|
||||
nostr-tools: 2.14.2(typescript@5.8.3)
|
||||
tippy.js: 6.3.7
|
||||
transitivePeerDependencies:
|
||||
@@ -6512,78 +6557,78 @@ snapshots:
|
||||
- tiptap-markdown
|
||||
- typescript
|
||||
|
||||
'@welshman/feeds@0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
|
||||
'@welshman/feeds@0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
|
||||
dependencies:
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/net': 0.3.8(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/relay': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/router': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/signer': 0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/net': 0.4.0(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/relay': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/router': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/signer': 0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
trava: 1.2.1
|
||||
transitivePeerDependencies:
|
||||
- nostr-signer-capacitor-plugin
|
||||
- typescript
|
||||
- ws
|
||||
|
||||
'@welshman/lib@0.3.8':
|
||||
'@welshman/lib@0.4.0':
|
||||
dependencies:
|
||||
'@scure/base': 1.2.6
|
||||
'@types/events': 3.0.3
|
||||
events: 3.3.0
|
||||
|
||||
'@welshman/net@0.3.8(typescript@5.8.3)(ws@8.18.3)':
|
||||
'@welshman/net@0.4.0(typescript@5.8.3)(ws@8.18.3)':
|
||||
dependencies:
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/relay': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/relay': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
events: 3.3.0
|
||||
isomorphic-ws: 5.0.0(ws@8.18.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
- ws
|
||||
|
||||
'@welshman/relay@0.3.8(typescript@5.8.3)':
|
||||
'@welshman/relay@0.4.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@welshman/router@0.3.8(typescript@5.8.3)':
|
||||
'@welshman/router@0.4.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/relay': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/relay': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@welshman/signer@0.3.8(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
|
||||
'@welshman/signer@0.4.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
|
||||
dependencies:
|
||||
'@noble/curves': 1.9.2
|
||||
'@noble/hashes': 1.8.0
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/net': 0.3.8(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/net': 0.4.0(typescript@5.8.3)(ws@8.18.3)
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
nostr-signer-capacitor-plugin: 0.0.4(@capacitor/core@7.2.0)
|
||||
nostr-tools: 2.14.2(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
- ws
|
||||
|
||||
'@welshman/store@0.3.8(typescript@5.8.3)':
|
||||
'@welshman/store@0.4.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/relay': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/util': 0.3.8(typescript@5.8.3)
|
||||
'@welshman/lib': 0.4.0
|
||||
'@welshman/relay': 0.4.0(typescript@5.8.3)
|
||||
'@welshman/util': 0.4.0(typescript@5.8.3)
|
||||
svelte: 4.2.20
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@welshman/util@0.3.8(typescript@5.8.3)':
|
||||
'@welshman/util@0.4.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@types/ws': 8.18.1
|
||||
'@welshman/lib': 0.3.8
|
||||
'@welshman/lib': 0.4.0
|
||||
js-base64: 3.7.7
|
||||
nostr-tools: 2.14.2(typescript@5.8.3)
|
||||
nostr-wasm: 0.1.0
|
||||
@@ -8415,11 +8460,11 @@ snapshots:
|
||||
|
||||
normalize-range@0.1.2: {}
|
||||
|
||||
nostr-editor@1.0.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.1)(nostr-tools@2.14.2(typescript@5.8.3))(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))):
|
||||
nostr-editor@1.0.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.1)(nostr-tools@2.14.2(typescript@5.8.3))(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))):
|
||||
dependencies:
|
||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-image': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-link': 2.23.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/extension-image': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
|
||||
'@tiptap/extension-link': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
|
||||
'@tiptap/pm': 2.12.0
|
||||
js-base64: 3.7.7
|
||||
light-bolt11-decoder: 3.2.0
|
||||
@@ -8435,6 +8480,18 @@ snapshots:
|
||||
dependencies:
|
||||
'@capacitor/core': 7.2.0
|
||||
|
||||
nostr-tools@2.12.0(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@noble/ciphers': 0.5.3
|
||||
'@noble/curves': 1.2.0
|
||||
'@noble/hashes': 1.3.1
|
||||
'@scure/base': 1.1.1
|
||||
'@scure/bip32': 1.3.1
|
||||
'@scure/bip39': 1.2.1
|
||||
optionalDependencies:
|
||||
nostr-wasm: 0.1.0
|
||||
typescript: 5.8.3
|
||||
|
||||
nostr-tools@2.14.2(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@noble/ciphers': 0.5.3
|
||||
@@ -8818,6 +8875,10 @@ snapshots:
|
||||
|
||||
q@1.5.1: {}
|
||||
|
||||
qr-scanner@1.4.2:
|
||||
dependencies:
|
||||
'@types/offscreencanvas': 2019.7.3
|
||||
|
||||
qrcode@1.5.4:
|
||||
dependencies:
|
||||
dijkstrajs: 1.0.3
|
||||
|
||||
+20
-3
@@ -1,3 +1,4 @@
|
||||
import {nwc} from "@getalby/sdk"
|
||||
import * as nip19 from "nostr-tools/nip19"
|
||||
import {get} from "svelte/store"
|
||||
import {randomId, flatten, poll, uniq, equals, TIMEZONE, LOCALE} from "@welshman/lib"
|
||||
@@ -55,6 +56,8 @@ import {
|
||||
getThunkError,
|
||||
} from "@welshman/app"
|
||||
import {
|
||||
wallet,
|
||||
getWebLn,
|
||||
PROTECTED,
|
||||
userMembership,
|
||||
INDEXER_RELAYS,
|
||||
@@ -242,9 +245,7 @@ export const checkRelayAccess = async (url: string, claim = "") => {
|
||||
if (error?.startsWith("mute: ")) return
|
||||
|
||||
// Ignore rejected empty claims
|
||||
if (!claim && error?.includes("invite code")) {
|
||||
return `failed to request access to relay`
|
||||
}
|
||||
if (!claim && error?.includes("invite code")) return
|
||||
|
||||
return message
|
||||
}
|
||||
@@ -445,3 +446,19 @@ export const makeAlert = async (params: AlertParams) => {
|
||||
|
||||
export const publishAlert = async (params: AlertParams) =>
|
||||
publishThunk({event: await makeAlert(params), relays: [NOTIFIER_RELAY]})
|
||||
|
||||
export const payInvoice = async (invoice: string) => {
|
||||
const $wallet = get(wallet)
|
||||
|
||||
if (!$wallet) {
|
||||
throw new Error("No wallet is connected")
|
||||
}
|
||||
|
||||
if ($wallet.type === "nwc") {
|
||||
return new nwc.NWCClient($wallet.info).payInvoice({invoice})
|
||||
} else if ($wallet.type === "webln") {
|
||||
return getWebLn()
|
||||
.enable()
|
||||
.then(() => getWebLn().sendPayment(invoice))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
import ThunkStatus from "@app/components/ThunkStatus.svelte"
|
||||
import ProfileDetail from "@app/components/ProfileDetail.svelte"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import ChannelMessageZapButton from "@app/components/ChannelMessageZapButton.svelte"
|
||||
import ChannelMessageEmojiButton from "@app/components/ChannelMessageEmojiButton.svelte"
|
||||
import ChannelMessageMenuButton from "@app/components/ChannelMessageMenuButton.svelte"
|
||||
import ChannelMessageMenuMobile from "@app/components/ChannelMessageMenuMobile.svelte"
|
||||
import {colors} from "@app/state"
|
||||
import {colors, ENABLE_ZAPS} from "@app/state"
|
||||
import {publishDelete, publishReaction} from "@app/commands"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
@@ -94,6 +95,9 @@
|
||||
<button
|
||||
class="join absolute right-1 top-1 border border-solid border-neutral text-xs opacity-0 transition-all"
|
||||
class:group-hover:opacity-100={!isMobile}>
|
||||
{#if ENABLE_ZAPS}
|
||||
<ChannelMessageZapButton {url} {event} />
|
||||
{/if}
|
||||
<ChannelMessageEmojiButton {url} {event} />
|
||||
{#if replyTo}
|
||||
<Button class="btn join-item btn-xs" onclick={reply}>
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import EmojiPicker from "@lib/components/EmojiPicker.svelte"
|
||||
import ZapButton from "@app/components/ZapButton.svelte"
|
||||
import EventInfo from "@app/components/EventInfo.svelte"
|
||||
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
||||
import {ENABLE_ZAPS} from "@app/state"
|
||||
import {publishReaction} from "@app/commands"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
@@ -40,6 +42,12 @@
|
||||
<Icon size={4} icon="smile-circle" />
|
||||
Send Reaction
|
||||
</Button>
|
||||
{#if ENABLE_ZAPS}
|
||||
<ZapButton replaceState {url} {event} class="btn btn-secondary w-full">
|
||||
<Icon size={4} icon="bolt" />
|
||||
Send Zap
|
||||
</ZapButton>
|
||||
{/if}
|
||||
<Button class="btn btn-neutral w-full" onclick={sendReply}>
|
||||
<Icon size={4} icon="reply" />
|
||||
Send Reply
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ZapButton from "@app/components/ZapButton.svelte"
|
||||
|
||||
const {url, event} = $props()
|
||||
</script>
|
||||
|
||||
<ZapButton {url} {event} class="btn join-item btn-xs">
|
||||
<Icon icon="bolt" size={4} />
|
||||
</ZapButton>
|
||||
@@ -52,7 +52,7 @@
|
||||
alt="Link preview"
|
||||
onerror={onError}
|
||||
src={imgproxy(preview.image)}
|
||||
class="bg-alt max-h-72 object-contain object-center" />
|
||||
class="bg-alt max-h-72 rounded-t-box object-contain object-center" />
|
||||
{/if}
|
||||
<div class="flex flex-col gap-2 p-4">
|
||||
<strong class="overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
|
||||
@@ -6,18 +6,21 @@
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Tippy from "@lib/components/Tippy.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ZapButton from "@app/components/ZapButton.svelte"
|
||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||
import EventMenu from "@app/components/EventMenu.svelte"
|
||||
import {ENABLE_ZAPS} from "@app/state"
|
||||
import {publishReaction} from "@app/commands"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
noun: string
|
||||
event: TrustedEvent
|
||||
hideZap?: boolean
|
||||
customActions?: Snippet
|
||||
}
|
||||
|
||||
const {url, noun, event, customActions}: Props = $props()
|
||||
const {url, noun, event, hideZap, customActions}: Props = $props()
|
||||
|
||||
const showPopover = () => popover?.show()
|
||||
|
||||
@@ -30,6 +33,11 @@
|
||||
</script>
|
||||
|
||||
<Button class="join rounded-full">
|
||||
{#if ENABLE_ZAPS && !hideZap}
|
||||
<ZapButton {url} {event} class="btn join-item btn-neutral btn-xs">
|
||||
<Icon icon="bolt" size={4} />
|
||||
</ZapButton>
|
||||
{/if}
|
||||
<EmojiButton {onEmoji} class="btn join-item btn-neutral btn-xs">
|
||||
<Icon icon="smile-circle" size={4} />
|
||||
</EmojiButton>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
import EventActions from "@app/components/EventActions.svelte"
|
||||
import {publishDelete, publishReaction} from "@app/commands"
|
||||
import {makeGoalPath} from "@app/routes"
|
||||
|
||||
interface Props {
|
||||
url: any
|
||||
event: any
|
||||
showActivity?: boolean
|
||||
}
|
||||
|
||||
const {url, event, showActivity = false}: Props = $props()
|
||||
|
||||
const path = makeGoalPath(url, event.id)
|
||||
|
||||
const deleteReaction = (event: TrustedEvent) => publishDelete({relays: [url], event})
|
||||
|
||||
const createReaction = (template: EventContent) =>
|
||||
publishReaction({...template, event, relays: [url]})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<div class="flex flex-grow flex-wrap justify-end gap-2">
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
|
||||
<ThunkStatusOrDeleted {event} />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
<EventActions {url} {event} hideZap noun="Goal" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,146 @@
|
||||
<script lang="ts">
|
||||
import {writable} from "svelte/store"
|
||||
import {makeEvent, ZAP_GOAL} from "@welshman/util"
|
||||
import {publishThunk} from "@welshman/app"
|
||||
import {isMobile, preventDefault} from "@lib/html"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import EditorContent from "@app/editor/EditorContent.svelte"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {PROTECTED} from "@app/state"
|
||||
import {makeEditor} from "@app/editor"
|
||||
|
||||
const {url} = $props()
|
||||
|
||||
const uploading = writable(false)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const selectFiles = () => editor.then(ed => ed.commands.selectFiles())
|
||||
|
||||
const submit = async () => {
|
||||
if ($uploading) return
|
||||
|
||||
if (!content) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Please provide a title for your funding goal.",
|
||||
})
|
||||
}
|
||||
|
||||
const ed = await editor
|
||||
const summary = ed.getText({blockSeparator: "\n"}).trim()
|
||||
|
||||
if (!summary.trim()) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Please provide details about your funding goal.",
|
||||
})
|
||||
}
|
||||
|
||||
const tags = [
|
||||
...ed.storage.nostr.getEditorTags(),
|
||||
["summary", summary],
|
||||
["amount", String(amount)],
|
||||
["relays", url],
|
||||
PROTECTED,
|
||||
]
|
||||
|
||||
publishThunk({
|
||||
relays: [url],
|
||||
event: makeEvent(ZAP_GOAL, {content, tags}),
|
||||
})
|
||||
|
||||
history.back()
|
||||
}
|
||||
|
||||
const editor = makeEditor({url, submit, uploading, placeholder: "What's on your mind?"})
|
||||
|
||||
let content = $state("")
|
||||
let amount = $state(1000)
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" onsubmit={preventDefault(submit)}>
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
<div>Create a Funding Goal</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<div>Request contributions for your fundraiser.</div>
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<div class="col-8 relative">
|
||||
<Field>
|
||||
{#snippet label()}
|
||||
<p>Title*</p>
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input
|
||||
autofocus={!isMobile}
|
||||
bind:value={content}
|
||||
class="grow"
|
||||
type="text"
|
||||
placeholder="What do funds go towards?" />
|
||||
</label>
|
||||
{/snippet}
|
||||
</Field>
|
||||
<div class="relative">
|
||||
<Field>
|
||||
{#snippet label()}
|
||||
<p>Details*</p>
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<div class="note-editor flex-grow overflow-hidden">
|
||||
<EditorContent {editor} />
|
||||
</div>
|
||||
{/snippet}
|
||||
</Field>
|
||||
<Button
|
||||
data-tip="Add an image"
|
||||
class="tooltip tooltip-left absolute bottom-1 right-2"
|
||||
onclick={selectFiles}>
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="paperclip" size={3} />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FieldInline>
|
||||
{#snippet label()}
|
||||
Goal Amount (sats)*
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<div class="flex flex-grow justify-end">
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<Icon icon="bolt" />
|
||||
<input bind:value={amount} type="number" class="w-28" />
|
||||
<p class="opacity-50">sats</p>
|
||||
</label>
|
||||
</div>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<input
|
||||
class="range range-primary -mt-2"
|
||||
type="range"
|
||||
min="1000"
|
||||
max="100000"
|
||||
step="1000"
|
||||
bind:value={amount} />
|
||||
</div>
|
||||
</div>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary">Create Goal</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {getTagValue} from "@welshman/util"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Content from "@app/components/Content.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import GoalActions from "@app/components/GoalActions.svelte"
|
||||
import GoalSummary from "@app/components/GoalSummary.svelte"
|
||||
import {makeGoalPath} from "@app/routes"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
|
||||
const summary = getTagValue("summary", event.tags)
|
||||
</script>
|
||||
|
||||
<Link class="col-2 card2 bg-alt w-full cursor-pointer" href={makeGoalPath(url, event.id)}>
|
||||
<p class="text-2xl">{event.content}</p>
|
||||
<Content
|
||||
event={{content: summary, tags: event.tags}}
|
||||
{url}
|
||||
expandMode="inline"
|
||||
minLength={50}
|
||||
maxLength={300} />
|
||||
<GoalSummary {url} {event} />
|
||||
<div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row">
|
||||
<span class="whitespace-nowrap py-1 text-sm opacity-75">
|
||||
Posted by <ProfileLink pubkey={event.pubkey} {url} />
|
||||
</span>
|
||||
<GoalActions showActivity {url} {event} />
|
||||
</div>
|
||||
</Link>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import {now, DAY, uniq, sum} from "@welshman/lib"
|
||||
import type {Zap, TrustedEvent} from "@welshman/util"
|
||||
import {getTagValue, fromMsats, ZAP_RESPONSE} from "@welshman/util"
|
||||
import {deriveEventsMapped} from "@welshman/store"
|
||||
import {repository, getValidZap} from "@welshman/app"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ZapButton from "@app/components/ZapButton.svelte"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
|
||||
const zaps = deriveEventsMapped<Zap>(repository, {
|
||||
filters: [{kinds: [ZAP_RESPONSE], "#e": [event.id]}],
|
||||
itemToEvent: item => item.response,
|
||||
eventToItem: (response: TrustedEvent) => getValidZap(response, event),
|
||||
})
|
||||
|
||||
const goalAmount = parseInt(getTagValue("amount", event.tags) || "0")
|
||||
const zapAmount = $derived(fromMsats(sum($zaps.map(zap => zap.invoiceAmount))))
|
||||
const contributorsCount = $derived(uniq($zaps.map(zap => zap.request.pubkey)).length)
|
||||
const daysOld = Math.ceil((now() - event.created_at) / DAY)
|
||||
</script>
|
||||
|
||||
<div class="card2 bg-alt flex flex-col gap-8">
|
||||
<div class="flex gap-8">
|
||||
<div>
|
||||
<p class="text-xl text-primary">{zapAmount} sats</p>
|
||||
<p class="text-sm opacity-75">funded of {goalAmount} sats</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xl">{contributorsCount}</p>
|
||||
<p class="text-sm opacity-75">{contributorsCount === 1 ? "contributor" : "contributors"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xl">{daysOld}</p>
|
||||
<p class="text-sm opacity-75">{daysOld === 1 ? "day" : "days"} old</p>
|
||||
</div>
|
||||
</div>
|
||||
<progress class="progress progress-primary" value={zapAmount} max={goalAmount}></progress>
|
||||
<ZapButton {url} {event} class="btn btn-primary lg:m-auto lg:px-20">
|
||||
<Icon icon="bolt" />
|
||||
Contribute to this goal
|
||||
</ZapButton>
|
||||
</div>
|
||||
@@ -1,31 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import {PLATFORM_NAME} from "@app/state"
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
<div>Where did my rooms go?</div>
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<p>
|
||||
You might have noticed that old rooms have disappeared from navigation. {PLATFORM_NAME} is still
|
||||
under heavy development, which means that we occasionally have to make breaking changes. In this
|
||||
case, we've changed how rooms work in {PLATFORM_NAME} to be more fully compatible with other NIP
|
||||
29 clients, like <Link external class="link" href="https://chachi.chat">Chachi</Link> and
|
||||
<Link external class="link" href="https://0xchat.com">0xChat</Link>.
|
||||
</p>
|
||||
<p>
|
||||
If you run a relay, please upgrade to a version that supports NIP 29. {PLATFORM_NAME} works best
|
||||
with the latest version of <Link
|
||||
external
|
||||
class="link"
|
||||
href="https://github.com/coracle-social/frith">Frith</Link
|
||||
>, which will automatically migrate your rooms. In the meantime, your messages are all still
|
||||
available under the "Chat" tab (all conversations have been temporarily merged together).
|
||||
</p>
|
||||
<Button class="btn btn-primary" onclick={() => history.back()}>Got it</Button>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import {deriveZapperForPubkey} from "@welshman/app"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
|
||||
const {pubkey} = $props()
|
||||
|
||||
const zapper = deriveZapperForPubkey(pubkey)
|
||||
|
||||
const back = () => history.back()
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
<div>Unable to Zap</div>
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<p>
|
||||
Zapping <ProfileLink {pubkey} class="!text-primary" /> isn't possible right now because
|
||||
{#if $zapper}
|
||||
their zap receiver isn't correctly set up.
|
||||
{:else}
|
||||
they don't currently have a zap receiver set up.
|
||||
{/if}
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
Go back
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
@@ -33,18 +33,20 @@
|
||||
const onSubmit = async () => {
|
||||
if (controller.loading) return
|
||||
|
||||
const {signerPubkey, connectSecret, relays} = Nip46Broker.parseBunkerUrl(controller.bunker)
|
||||
|
||||
if (!signerPubkey || relays.length === 0) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Sorry, it looks like that's an invalid bunker link.",
|
||||
})
|
||||
}
|
||||
|
||||
controller.loading = true
|
||||
|
||||
try {
|
||||
const {signerPubkey, connectSecret, relays} = Nip46Broker.parseBunkerUrl(controller.bunker)
|
||||
|
||||
console.log({signerPubkey, connectSecret, relays})
|
||||
|
||||
if (!signerPubkey || relays.length === 0) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Sorry, it looks like that's an invalid bunker link.",
|
||||
})
|
||||
}
|
||||
|
||||
controller.loading = true
|
||||
|
||||
const {clientSecret} = controller
|
||||
const broker = new Nip46Broker({relays, clientSecret, signerPubkey})
|
||||
const result = await broker.connect(connectSecret, NIP46_PERMS)
|
||||
@@ -64,6 +66,13 @@
|
||||
message: "Something went wrong, please try again!",
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Something went wrong, please try again!",
|
||||
})
|
||||
} finally {
|
||||
controller.loading = false
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
import Alerts from "@app/components/Alerts.svelte"
|
||||
import RoomCreate from "@app/components/RoomCreate.svelte"
|
||||
import MenuSpaceRoomItem from "@app/components/MenuSpaceRoomItem.svelte"
|
||||
import InfoMissingRooms from "@app/components/InfoMissingRooms.svelte"
|
||||
import {
|
||||
ENABLE_ZAPS,
|
||||
userRoomsByUrl,
|
||||
hasMembershipUrl,
|
||||
memberships,
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
const relay = deriveRelay(url)
|
||||
const chatPath = makeSpacePath(url, "chat")
|
||||
const goalsPath = makeSpacePath(url, "goals")
|
||||
const threadsPath = makeSpacePath(url, "threads")
|
||||
const calendarPath = makeSpacePath(url, "calendar")
|
||||
const userRooms = deriveUserRooms(url)
|
||||
@@ -49,8 +50,6 @@
|
||||
showMenu = !showMenu
|
||||
}
|
||||
|
||||
const showMissingRooms = () => pushModal(InfoMissingRooms)
|
||||
|
||||
const showMembers = () =>
|
||||
pushModal(
|
||||
ProfileList,
|
||||
@@ -129,10 +128,18 @@
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex min-h-0 flex-col gap-1 overflow-auto">
|
||||
<div class="flex max-h-[calc(100vh-150px)] min-h-0 flex-col gap-1 overflow-auto">
|
||||
<SecondaryNavItem {replaceState} href={makeSpacePath(url)}>
|
||||
<Icon icon="home-smile" /> Home
|
||||
</SecondaryNavItem>
|
||||
{#if ENABLE_ZAPS}
|
||||
<SecondaryNavItem
|
||||
{replaceState}
|
||||
href={goalsPath}
|
||||
notification={$notifications.has(goalsPath)}>
|
||||
<Icon icon="star-fall-minimalistic-2" /> Goals
|
||||
</SecondaryNavItem>
|
||||
{/if}
|
||||
<SecondaryNavItem
|
||||
{replaceState}
|
||||
href={threadsPath}
|
||||
@@ -177,10 +184,6 @@
|
||||
notification={$notifications.has(chatPath)}>
|
||||
<Icon icon="chat-round" /> Chat
|
||||
</SecondaryNavItem>
|
||||
<Button class="link flex items-center gap-2 py-2 pl-4 text-sm" onclick={showMissingRooms}>
|
||||
<Icon icon="info-circle" size={4} />
|
||||
Where did my rooms go?
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</SecondaryNavSection>
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
type Props = {
|
||||
pubkey: string
|
||||
url?: string
|
||||
class?: string
|
||||
unstyled?: boolean
|
||||
}
|
||||
|
||||
const {pubkey, url, unstyled}: Props = $props()
|
||||
const {pubkey, url, unstyled, ...props}: Props = $props()
|
||||
|
||||
const openProfile = () => pushModal(ProfileDetail, {pubkey, url})
|
||||
</script>
|
||||
|
||||
<Button onclick={preventDefault(openProfile)} class={cx({"link-content": !unstyled})}>
|
||||
<Button onclick={preventDefault(openProfile)} class={cx(props.class, {"link-content": !unstyled})}>
|
||||
@<ProfileName {pubkey} {url} />
|
||||
</Button>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import type {Snippet} from "svelte"
|
||||
import {groupBy, uniq, uniqBy, batch, displayList} from "@welshman/lib"
|
||||
import {groupBy, sum, uniq, uniqBy, batch, displayList} from "@welshman/lib"
|
||||
import {
|
||||
REACTION,
|
||||
ZAP_RESPONSE,
|
||||
getReplyFilters,
|
||||
getEmojiTags,
|
||||
getEmojiTag,
|
||||
fromMsats,
|
||||
getTag,
|
||||
REPORT,
|
||||
DELETE,
|
||||
} from "@welshman/util"
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {deriveEvents} from "@welshman/store"
|
||||
import type {TrustedEvent, EventContent, Zap} from "@welshman/util"
|
||||
import {deriveEvents, deriveEventsMapped} from "@welshman/store"
|
||||
import {load} from "@welshman/net"
|
||||
import {pubkey, repository, displayProfileByPubkey} from "@welshman/app"
|
||||
import {pubkey, repository, getValidZap, displayProfileByPubkey} from "@welshman/app"
|
||||
import {isMobile, preventDefault, stopPropagation} from "@lib/html"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Reaction from "@app/components/Reaction.svelte"
|
||||
import EventReportDetails from "@app/components/EventReportDetails.svelte"
|
||||
import {REACTION_KINDS} from "@app/state"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
interface Props {
|
||||
@@ -49,6 +52,12 @@
|
||||
filters: [{kinds: [REACTION], "#e": [event.id]}],
|
||||
})
|
||||
|
||||
const zaps = deriveEventsMapped<Zap>(repository, {
|
||||
filters: [{kinds: [ZAP_RESPONSE], "#e": [event.id]}],
|
||||
itemToEvent: item => item.response,
|
||||
eventToItem: (response: TrustedEvent) => getValidZap(response, event),
|
||||
})
|
||||
|
||||
const onReactionClick = (events: TrustedEvent[]) => {
|
||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
||||
|
||||
@@ -77,6 +86,8 @@
|
||||
),
|
||||
)
|
||||
|
||||
const groupedZaps = $derived(groupBy(e => getReactionKey(e.request), $zaps))
|
||||
|
||||
onMount(() => {
|
||||
const controller = new AbortController()
|
||||
|
||||
@@ -84,7 +95,7 @@
|
||||
load({
|
||||
relays: [url],
|
||||
signal: controller.signal,
|
||||
filters: getReplyFilters([event], {kinds: [REACTION, REPORT, DELETE]}),
|
||||
filters: getReplyFilters([event], {kinds: [REPORT, DELETE, ...REACTION_KINDS]}),
|
||||
onEvent: batch(300, (events: TrustedEvent[]) => {
|
||||
load({
|
||||
relays: [url],
|
||||
@@ -100,7 +111,7 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if $reactions.length > 0 || $reports.length > 0}
|
||||
{#if $reactions.length > 0 || $zaps.length || $reports.length > 0}
|
||||
<div class="flex min-w-0 flex-wrap gap-2">
|
||||
{#if url && $reports.length > 0}
|
||||
<button
|
||||
@@ -113,6 +124,24 @@
|
||||
<span>{$reports.length}</span>
|
||||
</button>
|
||||
{/if}
|
||||
{#each groupedZaps.entries() as [key, zaps]}
|
||||
{@const amount = fromMsats(sum(zaps.map(zap => zap.invoiceAmount)))}
|
||||
{@const pubkeys = uniq(zaps.map(zap => zap.request.pubkey))}
|
||||
{@const isOwn = $pubkey && pubkeys.includes($pubkey)}
|
||||
{@const info = displayList(pubkeys.map(pubkey => displayProfileByPubkey(pubkey)))}
|
||||
{@const tooltip = `${info} zapped`}
|
||||
<button
|
||||
type="button"
|
||||
data-tip={tooltip}
|
||||
class="flex-inline btn btn-neutral btn-xs gap-1 rounded-full {reactionClass}"
|
||||
class:tooltip={!noTooltip && !isMobile}
|
||||
class:border={isOwn}
|
||||
class:border-solid={isOwn}
|
||||
class:border-primary={isOwn}>
|
||||
<Reaction event={zaps[0].request} />
|
||||
<span>{amount}</span>
|
||||
</button>
|
||||
{/each}
|
||||
{#each groupedReactions.entries() as [key, events]}
|
||||
{@const pubkeys = events.map(e => e.pubkey)}
|
||||
{@const isOwn = $pubkey && pubkeys.includes($pubkey)}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<script lang="ts">
|
||||
import {debounce} from "throttle-debounce"
|
||||
import {nwc} from "@getalby/sdk"
|
||||
import {sleep} from "@welshman/lib"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Scanner from "@lib/components/Scanner.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
import Divider from "@lib/components/Divider.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import type {NWCInfo} from "@app/state"
|
||||
import {wallet, getWebLn} from "@app/state"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const connectWithWebLn = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
await Promise.all([sleep(800), getWebLn().enable()])
|
||||
const info = await getWebLn().getInfo()
|
||||
|
||||
if (!info?.supports?.includes("lightning")) {
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: "Your extension does not support lightning payments",
|
||||
})
|
||||
} else {
|
||||
wallet.set({type: "webln", info})
|
||||
pushToast({message: "Wallet successfully connected!"})
|
||||
|
||||
await sleep(400)
|
||||
|
||||
back()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: "Wallet failed to connect",
|
||||
})
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const connectWithNWC = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const client = new nwc.NWCClient({nostrWalletConnectUrl})
|
||||
const [_, info] = await Promise.all([sleep(800), client.getInfo()])
|
||||
|
||||
if (!info) {
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: "Wallet failed to connect",
|
||||
})
|
||||
} else {
|
||||
wallet.set({type: "nwc", info: client.options as unknown as NWCInfo})
|
||||
pushToast({message: "Wallet successfully connected!"})
|
||||
|
||||
await sleep(400)
|
||||
|
||||
back()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: "Wallet failed to connect",
|
||||
})
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const toggleScanner = () => {
|
||||
showScanner = !showScanner
|
||||
}
|
||||
|
||||
const onScan = debounce(1000, async (data: string) => {
|
||||
showScanner = false
|
||||
nostrWalletConnectUrl = data
|
||||
await connectWithNWC()
|
||||
})
|
||||
|
||||
let nostrWalletConnectUrl = $state("")
|
||||
let showScanner = $state(false)
|
||||
let loading = $state(false)
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
<div>Connect a Wallet</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
Use Nostr Wallet Connect to send Bitcoin payments over Lightning.
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
{#if getWebLn()}
|
||||
<Button
|
||||
class="btn btn-primary"
|
||||
disabled={Boolean(nostrWalletConnectUrl || loading)}
|
||||
onclick={connectWithWebLn}>
|
||||
<Spinner loading={!nostrWalletConnectUrl && loading}>
|
||||
{#if !nostrWalletConnectUrl && loading}
|
||||
Connecting...
|
||||
{:else}
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon icon="cpu" />
|
||||
Connect with WebLN
|
||||
</div>
|
||||
{/if}
|
||||
</Spinner>
|
||||
</Button>
|
||||
<Divider>Or</Divider>
|
||||
{/if}
|
||||
<Field>
|
||||
{#snippet label()}
|
||||
Connection Secret*
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<label class="input input-bordered flex w-full items-center gap-2">
|
||||
<Icon icon="lock" />
|
||||
<input
|
||||
bind:value={nostrWalletConnectUrl}
|
||||
autocomplete="off"
|
||||
name="flotilla-nwc"
|
||||
class="grow"
|
||||
type="password" />
|
||||
<Button onclick={toggleScanner}>
|
||||
<Icon icon="qr-code" />
|
||||
</Button>
|
||||
</label>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
You can find this in any wallet that supports
|
||||
<Link external href="https://nwc.getalby.com/about" class="text-primary"
|
||||
>Nostr Wallet Connect</Link
|
||||
>.
|
||||
{/snippet}
|
||||
</Field>
|
||||
{#if showScanner}
|
||||
<Scanner onscan={onScan} />
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
Go back
|
||||
</Button>
|
||||
<Button
|
||||
class="btn btn-primary"
|
||||
disabled={!nostrWalletConnectUrl || loading}
|
||||
onclick={connectWithNWC}>
|
||||
<Spinner loading={Boolean(nostrWalletConnectUrl && loading)}>
|
||||
{#if nostrWalletConnectUrl && loading}
|
||||
Connecting...
|
||||
{:else}
|
||||
<div class="flex items-center gap-2">
|
||||
Connect Wallet
|
||||
<Icon icon="alt-arrow-right" />
|
||||
</div>
|
||||
{/if}
|
||||
</Spinner>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import Confirm from "@lib/components/Confirm.svelte"
|
||||
import {wallet} from "@app/state"
|
||||
import {clearModals} from "@app/modal"
|
||||
|
||||
const confirm = async () => {
|
||||
wallet.set(undefined)
|
||||
|
||||
clearModals()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Confirm
|
||||
{confirm}
|
||||
title="Disconnect Wallet"
|
||||
message="Are you sure you want to disconnect your bitcoin wallet?" />
|
||||
@@ -0,0 +1,166 @@
|
||||
<script lang="ts">
|
||||
import type {NativeEmoji} from "emoji-picker-element/shared"
|
||||
import {signer, deriveZapperForPubkey} from "@welshman/app"
|
||||
import {load} from "@welshman/net"
|
||||
import {Router} from "@welshman/router"
|
||||
import {requestZap, makeZapRequest, getZapResponseFilter} from "@welshman/util"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import EmojiButton from "@lib/components/EmojiButton.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import {payInvoice} from "@app/commands"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
pubkey: string
|
||||
eventId?: string
|
||||
}
|
||||
|
||||
const {url, pubkey, eventId}: Props = $props()
|
||||
|
||||
const minPos = 1
|
||||
const maxPos = 1000
|
||||
const minVal = 21
|
||||
const maxVal = 1000000
|
||||
const zapperStore = deriveZapperForPubkey(pubkey)
|
||||
|
||||
const posToAmount = (pos: number) => {
|
||||
const normalizedPos = (pos - minPos) / (maxPos - minPos)
|
||||
const logMin = Math.log(minVal)
|
||||
const logMax = Math.log(maxVal)
|
||||
const logValue = logMin + normalizedPos * (logMax - logMin)
|
||||
return Math.round(Math.exp(logValue))
|
||||
}
|
||||
|
||||
const amountToPos = (amount: number) => {
|
||||
const clampedAmount = Math.max(minVal, Math.min(maxVal, amount))
|
||||
const logMin = Math.log(minVal)
|
||||
const logMax = Math.log(maxVal)
|
||||
const logValue = Math.log(clampedAmount)
|
||||
const normalizedPos = (logValue - logMin) / (logMax - logMin)
|
||||
return Math.round(minPos + normalizedPos * (maxPos - minPos))
|
||||
}
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const onEmoji = (emoji: NativeEmoji) => {
|
||||
content = emoji.unicode
|
||||
}
|
||||
|
||||
const sendZap = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const zapper = $zapperStore!
|
||||
const msats = amount * 1000
|
||||
const relays = url ? [url] : Router.get().ForPubkey(pubkey).getUrls()
|
||||
const filters = [getZapResponseFilter({zapper, pubkey, eventId})]
|
||||
const params = {pubkey, content, eventId, msats, relays, zapper}
|
||||
const event = await $signer!.sign(makeZapRequest(params))
|
||||
const res = await requestZap({zapper, event})
|
||||
|
||||
if (!res.invoice) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to zap: ${res.error || "no error given"}`,
|
||||
})
|
||||
}
|
||||
|
||||
await payInvoice(res.invoice)
|
||||
await load({relays, filters})
|
||||
|
||||
pushToast({message: "Zap successfully sent!"})
|
||||
back()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
const message = String(e).replace(/^.*Error: /, "")
|
||||
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to zap: ${message}`,
|
||||
})
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
let pos = $state(minPos)
|
||||
let amount = $state(minVal)
|
||||
let content = $state("⚡️")
|
||||
let loading = $state(false)
|
||||
|
||||
$effect(() => {
|
||||
amount = posToAmount(pos)
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
const newPos = amountToPos(amount)
|
||||
if (newPos !== pos) {
|
||||
pos = newPos
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
<div>Send a Zap</div>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<div>To <ProfileLink {pubkey} class="!text-primary" /></div>
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<FieldInline class="!grid-cols-3">
|
||||
{#snippet label()}
|
||||
Emoji Reaction
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<div class="flex flex-grow items-center justify-end gap-4">
|
||||
<EmojiButton {onEmoji} class="btn btn-neutral">
|
||||
{content}
|
||||
</EmojiButton>
|
||||
</div>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<FieldInline class="!grid-cols-3">
|
||||
{#snippet label()}
|
||||
Amount
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<div class="flex flex-grow justify-end">
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<Icon icon="bolt" />
|
||||
<input bind:value={amount} type="number" class="w-24" />
|
||||
</label>
|
||||
</div>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<input
|
||||
class="range range-primary -mt-2"
|
||||
type="range"
|
||||
min={minPos}
|
||||
max={maxPos}
|
||||
bind:value={pos} />
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" onclick={sendZap} disabled={loading}>
|
||||
<Spinner {loading}>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if !loading}
|
||||
<Icon icon="bolt" />
|
||||
{/if}
|
||||
Send Zap
|
||||
</div>
|
||||
</Spinner>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import type {Snippet} from "svelte"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {deriveZapperForPubkey} from "@welshman/app"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Zap from "@app/components/Zap.svelte"
|
||||
import InfoZapperError from "@app/components/InfoZapperError.svelte"
|
||||
import WalletConnect from "@app/components/WalletConnect.svelte"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {wallet} from "@app/state"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
children: Snippet
|
||||
replaceState?: boolean
|
||||
class?: string
|
||||
}
|
||||
|
||||
const {url, event, children, replaceState, ...props}: Props = $props()
|
||||
|
||||
const zapper = deriveZapperForPubkey(event.pubkey)
|
||||
|
||||
const onClick = () => {
|
||||
if (!$zapper?.allowsNostr) {
|
||||
pushModal(InfoZapperError, {url, pubkey: event.pubkey, eventId: event.id}, {replaceState})
|
||||
} else if ($wallet) {
|
||||
pushModal(Zap, {url, pubkey: event.pubkey, eventId: event.id}, {replaceState})
|
||||
} else {
|
||||
pushModal(WalletConnect, {}, {replaceState})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button onclick={onClick} {...props}>
|
||||
{@render children?.()}
|
||||
</Button>
|
||||
+29
-19
@@ -1,6 +1,6 @@
|
||||
import {derived} from "svelte/store"
|
||||
import {synced, throttled} from "@welshman/store"
|
||||
import {pubkey} from "@welshman/app"
|
||||
import {synced, localStorageProvider, throttled} from "@welshman/store"
|
||||
import {pubkey, relaysByUrl} from "@welshman/app"
|
||||
import {prop, spec, identity, now, groupBy} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {EVENT_TIME, MESSAGE, THREAD, COMMENT, getTagValue} from "@welshman/util"
|
||||
@@ -12,11 +12,15 @@ import {
|
||||
makeSpaceChatPath,
|
||||
makeRoomPath,
|
||||
} from "@app/routes"
|
||||
import {chats, getUrlsForEvent, userRoomsByUrl, repositoryStore} from "@app/state"
|
||||
import {chats, hasNip29, getUrlsForEvent, userRoomsByUrl, repositoryStore} from "@app/state"
|
||||
|
||||
// Checked state
|
||||
|
||||
export const checked = synced<Record<string, number>>("checked", {})
|
||||
export const checked = synced<Record<string, number>>({
|
||||
key: "checked",
|
||||
defaultValue: {},
|
||||
storage: localStorageProvider,
|
||||
})
|
||||
|
||||
export const deriveChecked = (key: string) => derived(checked, prop(key))
|
||||
|
||||
@@ -27,9 +31,12 @@ export const setChecked = (key: string) => checked.update(state => ({...state, [
|
||||
export const notifications = derived(
|
||||
throttled(
|
||||
1000,
|
||||
derived([pubkey, checked, chats, userRoomsByUrl, repositoryStore, getUrlsForEvent], identity),
|
||||
derived(
|
||||
[pubkey, checked, chats, userRoomsByUrl, repositoryStore, getUrlsForEvent, relaysByUrl],
|
||||
identity,
|
||||
),
|
||||
),
|
||||
([$pubkey, $checked, $chats, $userRoomsByUrl, $repository, $getUrlsForEvent]) => {
|
||||
([$pubkey, $checked, $chats, $userRoomsByUrl, $repository, $getUrlsForEvent, $relaysByUrl]) => {
|
||||
const hasNotification = (path: string, latestEvent: TrustedEvent | undefined) => {
|
||||
if (!latestEvent || latestEvent.pubkey === $pubkey) {
|
||||
return false
|
||||
@@ -91,11 +98,6 @@ export const notifications = derived(
|
||||
paths.add(calendarPath)
|
||||
}
|
||||
|
||||
if (hasNotification(messagesPath, messagesEvents[0])) {
|
||||
paths.add(spacePath)
|
||||
paths.add(messagesPath)
|
||||
}
|
||||
|
||||
const commentsByThreadId = groupBy(
|
||||
e => getTagValue("E", e.tags),
|
||||
threadEvents.filter(spec({kind: COMMENT})),
|
||||
@@ -122,16 +124,24 @@ export const notifications = derived(
|
||||
}
|
||||
}
|
||||
|
||||
for (const room of rooms) {
|
||||
const roomPath = makeRoomPath(url, room)
|
||||
const latestEvent = allMessageEvents.find(
|
||||
e =>
|
||||
$getUrlsForEvent(e.id).includes(url) && e.tags.find(t => t[0] === "h" && t[1] === room),
|
||||
)
|
||||
if (hasNip29($relaysByUrl.get(url))) {
|
||||
for (const room of rooms) {
|
||||
const roomPath = makeRoomPath(url, room)
|
||||
const latestEvent = allMessageEvents.find(
|
||||
e =>
|
||||
$getUrlsForEvent(e.id).includes(url) &&
|
||||
e.tags.find(t => t[0] === "h" && t[1] === room),
|
||||
)
|
||||
|
||||
if (hasNotification(roomPath, latestEvent)) {
|
||||
if (hasNotification(roomPath, latestEvent)) {
|
||||
paths.add(spacePath)
|
||||
paths.add(roomPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (hasNotification(messagesPath, messagesEvents[0])) {
|
||||
paths.add(spacePath)
|
||||
paths.add(roomPath)
|
||||
paths.add(messagesPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ export const getWebPushInfo = async () => {
|
||||
}
|
||||
|
||||
if (!("PushManager" in window)) {
|
||||
throw new Error("Push messaging not supported")
|
||||
throw new Error("Push notifications are not supported")
|
||||
}
|
||||
|
||||
if (Notification.permission === "denied") {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
DIRECT_MESSAGE_FILE,
|
||||
MESSAGE,
|
||||
THREAD,
|
||||
ZAP_GOAL,
|
||||
EVENT_TIME,
|
||||
} from "@welshman/util"
|
||||
import {makeChatId, entityLink, decodeRelay, encodeRelay, userRoomsByUrl, ROOM} from "@app/state"
|
||||
@@ -37,6 +38,8 @@ export const makeRoomPath = (url: string, room: string) => `/spaces/${encodeRela
|
||||
|
||||
export const makeSpaceChatPath = (url: string) => makeRoomPath(url, "chat")
|
||||
|
||||
export const makeGoalPath = (url: string, eventId?: string) => makeSpacePath(url, "goals", eventId)
|
||||
|
||||
export const makeThreadPath = (url: string, eventId?: string) =>
|
||||
makeSpacePath(url, "threads", eventId)
|
||||
|
||||
@@ -87,6 +90,10 @@ export const getEventPath = async (event: TrustedEvent, urls: string[]) => {
|
||||
if (urls.length > 0) {
|
||||
const url = urls[0]
|
||||
|
||||
if (event.kind === ZAP_GOAL) {
|
||||
return makeGoalPath(url, event.id)
|
||||
}
|
||||
|
||||
if (event.kind === THREAD) {
|
||||
return makeThreadPath(url, event.id)
|
||||
}
|
||||
@@ -103,6 +110,10 @@ export const getEventPath = async (event: TrustedEvent, urls: string[]) => {
|
||||
const id = event.tags.find(nthEq(0, "E"))?.[1]
|
||||
|
||||
if (id && kind) {
|
||||
if (parseInt(kind) === ZAP_GOAL) {
|
||||
return makeGoalPath(url, id)
|
||||
}
|
||||
|
||||
if (parseInt(kind) === THREAD) {
|
||||
return makeThreadPath(url, id)
|
||||
}
|
||||
|
||||
+50
-4
@@ -1,4 +1,5 @@
|
||||
import twColors from "tailwindcss/colors"
|
||||
import {Capacitor} from "@capacitor/core"
|
||||
import {get, derived} from "svelte/store"
|
||||
import * as nip19 from "nostr-tools/nip19"
|
||||
import {
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
deriveEventsMapped,
|
||||
withGetter,
|
||||
synced,
|
||||
localStorageProvider,
|
||||
} from "@welshman/store"
|
||||
import {
|
||||
getIdFilters,
|
||||
@@ -37,6 +39,7 @@ import {
|
||||
CLIENT_AUTH,
|
||||
AUTH_JOIN,
|
||||
REACTION,
|
||||
ZAP_REQUEST,
|
||||
ZAP_RESPONSE,
|
||||
DIRECT_MESSAGE,
|
||||
DIRECT_MESSAGE_FILE,
|
||||
@@ -96,6 +99,10 @@ export const ROOM = "h"
|
||||
|
||||
export const PROTECTED = ["-"]
|
||||
|
||||
export const ENABLE_ZAPS = Capacitor.getPlatform() != "ios"
|
||||
|
||||
export const REACTION_KINDS = ENABLE_ZAPS ? [REACTION, ZAP_RESPONSE] : [REACTION]
|
||||
|
||||
export const NOTIFIER_PUBKEY = import.meta.env.VITE_NOTIFIER_PUBKEY
|
||||
|
||||
export const NOTIFIER_RELAY = import.meta.env.VITE_NOTIFIER_RELAY
|
||||
@@ -130,11 +137,9 @@ export const DUFFLEPUD_URL = "https://dufflepud.onrender.com"
|
||||
|
||||
export const IMGPROXY_URL = "https://imgproxy.coracle.social"
|
||||
|
||||
export const REACTION_KINDS = [REACTION, ZAP_RESPONSE]
|
||||
|
||||
export const NIP46_PERMS =
|
||||
"nip44_encrypt,nip44_decrypt," +
|
||||
[CLIENT_AUTH, AUTH_JOIN, MESSAGE, THREAD, COMMENT, ROOMS, WRAP, REACTION]
|
||||
[CLIENT_AUTH, AUTH_JOIN, MESSAGE, THREAD, COMMENT, ROOMS, WRAP, REACTION, ZAP_REQUEST]
|
||||
.map(k => `sign_event:${k}`)
|
||||
.join(",")
|
||||
|
||||
@@ -301,7 +306,11 @@ routerContext.getIndexerRelays = always(INDEXER_RELAYS)
|
||||
|
||||
// Settings
|
||||
|
||||
export const canDecrypt = synced("canDecrypt", false)
|
||||
export const canDecrypt = synced({
|
||||
key: "canDecrypt",
|
||||
defaultValue: false,
|
||||
storage: localStorageProvider,
|
||||
})
|
||||
|
||||
export const SETTINGS = 38489
|
||||
|
||||
@@ -346,6 +355,43 @@ export const {
|
||||
load: makeOutboxLoader(SETTINGS),
|
||||
})
|
||||
|
||||
// Wallets
|
||||
|
||||
export type WebLNInfo = {
|
||||
methods?: string[]
|
||||
supports?: string[]
|
||||
version?: string
|
||||
node?: {
|
||||
alias: string
|
||||
}
|
||||
}
|
||||
|
||||
export type NWCInfo = {
|
||||
lud16: string
|
||||
secret: string
|
||||
relayUrl: string
|
||||
walletPubkey: string
|
||||
nostrWalletConnectUrl: string
|
||||
}
|
||||
|
||||
export type Wallet =
|
||||
| {
|
||||
type: "webln"
|
||||
info: WebLNInfo
|
||||
}
|
||||
| {
|
||||
type: "nwc"
|
||||
info: NWCInfo
|
||||
}
|
||||
|
||||
export const wallet = synced<Wallet | undefined>({
|
||||
key: "wallet",
|
||||
defaultValue: undefined,
|
||||
storage: localStorageProvider,
|
||||
})
|
||||
|
||||
export const getWebLn = () => (window as any).webln
|
||||
|
||||
// Alerts
|
||||
|
||||
export type Alert = {
|
||||
|
||||
+6
-2
@@ -1,3 +1,7 @@
|
||||
import {synced} from "@welshman/store"
|
||||
import {synced, localStorageProvider} from "@welshman/store"
|
||||
|
||||
export const theme = synced<string>("theme", "dark")
|
||||
export const theme = synced({
|
||||
key: "theme",
|
||||
defaultValue: "dark",
|
||||
storage: localStorageProvider,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 16.9C2 15.5906 2 14.9359 2.29472 14.455C2.45963 14.1859 2.68589 13.9596 2.955 13.7947C3.43594 13.5 4.09063 13.5 5.4 13.5H6.5C8.38562 13.5 9.32843 13.5 9.91421 14.0858C10.5 14.6716 10.5 15.6144 10.5 17.5V18.6C10.5 19.9094 10.5 20.5641 10.2053 21.045C10.0404 21.3141 9.81411 21.5404 9.545 21.7053C9.06406 22 8.40937 22 7.1 22C5.13594 22 4.15391 22 3.4325 21.5579C3.02884 21.3106 2.68945 20.9712 2.44208 20.5675C2 19.8461 2 18.8641 2 16.9Z" stroke="#1C274C" stroke-width="1.5"/>
|
||||
<path d="M13.5 5.4C13.5 4.09063 13.5 3.43594 13.7947 2.955C13.9596 2.68589 14.1859 2.45963 14.455 2.29472C14.9359 2 15.5906 2 16.9 2C18.8641 2 19.8461 2 20.5675 2.44208C20.9712 2.68945 21.3106 3.02884 21.5579 3.4325C22 4.15391 22 5.13594 22 7.1C22 8.40937 22 9.06406 21.7053 9.545C21.5404 9.81411 21.3141 10.0404 21.045 10.2053C20.5641 10.5 19.9094 10.5 18.6 10.5H17.5C15.6144 10.5 14.6716 10.5 14.0858 9.91421C13.5 9.32843 13.5 8.38562 13.5 6.5V5.4Z" stroke="#1C274C" stroke-width="1.5"/>
|
||||
<path d="M16.5 6.25C16.5 5.73459 16.5 5.47689 16.6291 5.29493C16.6747 5.23072 16.7307 5.17466 16.7949 5.12911C16.9769 5 17.2346 5 17.75 5C18.2654 5 18.5231 5 18.7051 5.12911C18.7693 5.17466 18.8253 5.23072 18.8709 5.29493C19 5.47689 19 5.73459 19 6.25C19 6.76541 19 7.02311 18.8709 7.20507C18.8253 7.26928 18.7693 7.32534 18.7051 7.37089C18.5231 7.5 18.2654 7.5 17.75 7.5C17.2346 7.5 16.9769 7.5 16.7949 7.37089C16.7307 7.32534 16.6747 7.26928 16.6291 7.20507C16.5 7.02311 16.5 6.76541 16.5 6.25Z" fill="#1C274C"/>
|
||||
<path d="M12.75 22C12.75 22.4142 13.0858 22.75 13.5 22.75C13.9142 22.75 14.25 22.4142 14.25 22H12.75ZM14.3889 13.8371L14.8055 14.4607L14.8055 14.4607L14.3889 13.8371ZM13.8371 14.3889L13.2135 13.9722L13.2135 13.9722L13.8371 14.3889ZM19 12.75H17V14.25H19V12.75ZM12.75 19V22H14.25V19H12.75ZM17 12.75C16.3134 12.75 15.742 12.7491 15.281 12.796C14.8075 12.8441 14.3682 12.9489 13.9722 13.2135L14.8055 14.4607C14.914 14.3882 15.078 14.3244 15.4328 14.2883C15.8002 14.2509 16.2822 14.25 17 14.25V12.75ZM14.25 17C14.25 16.2822 14.2509 15.8002 14.2883 15.4328C14.3244 15.078 14.3882 14.914 14.4607 14.8055L13.2135 13.9722C12.9489 14.3682 12.8441 14.8075 12.796 15.281C12.7491 15.742 12.75 16.3134 12.75 17H14.25ZM13.9722 13.2135C13.6719 13.4141 13.4141 13.6719 13.2135 13.9722L14.4607 14.8055C14.5519 14.669 14.669 14.5519 14.8055 14.4607L13.9722 13.2135Z" fill="#1C274C"/>
|
||||
<path d="M22.75 13.5C22.75 13.0858 22.4142 12.75 22 12.75C21.5858 12.75 21.25 13.0858 21.25 13.5H22.75ZM20.7654 21.8478L21.0524 22.5407L21.0524 22.5407L20.7654 21.8478ZM21.8478 20.7654L21.1548 20.4784V20.4784L21.8478 20.7654ZM17 22.75H19V21.25H17V22.75ZM22.75 17V13.5H21.25V17H22.75ZM19 22.75C19.4557 22.75 19.835 22.7504 20.1454 22.7292C20.4625 22.7076 20.762 22.661 21.0524 22.5407L20.4784 21.1548C20.4012 21.1868 20.284 21.2163 20.0433 21.2327C19.7958 21.2496 19.4762 21.25 19 21.25V22.75ZM21.25 19C21.25 19.4762 21.2496 19.7958 21.2327 20.0433C21.2163 20.284 21.1868 20.4012 21.1548 20.4784L22.5407 21.0524C22.661 20.762 22.7076 20.4625 22.7292 20.1454C22.7504 19.835 22.75 19.4557 22.75 19H21.25ZM21.0524 22.5407C21.7262 22.2616 22.2616 21.7262 22.5407 21.0524L21.1548 20.4784C21.028 20.7846 20.7846 21.028 20.4784 21.1549L21.0524 22.5407Z" fill="#1C274C"/>
|
||||
<path d="M2 7.1C2 5.13594 2 4.15391 2.44208 3.4325C2.68945 3.02884 3.02884 2.68945 3.4325 2.44208C4.15391 2 5.13594 2 7.1 2C8.40937 2 9.06406 2 9.545 2.29472C9.81411 2.45963 10.0404 2.68589 10.2053 2.955C10.5 3.43594 10.5 4.09063 10.5 5.4V6.5C10.5 8.38562 10.5 9.32843 9.91421 9.91421C9.32843 10.5 8.38562 10.5 6.5 10.5H5.4C4.09063 10.5 3.43594 10.5 2.955 10.2053C2.68589 10.0404 2.45963 9.81411 2.29472 9.545C2 9.06406 2 8.40937 2 7.1Z" stroke="#1C274C" stroke-width="1.5"/>
|
||||
<path d="M5 6.25C5 5.73459 5 5.47689 5.12911 5.29493C5.17466 5.23072 5.23072 5.17466 5.29493 5.12911C5.47689 5 5.73459 5 6.25 5C6.76541 5 7.02311 5 7.20507 5.12911C7.26928 5.17466 7.32534 5.23072 7.37089 5.29493C7.5 5.47689 7.5 5.73459 7.5 6.25C7.5 6.76541 7.5 7.02311 7.37089 7.20507C7.32534 7.26928 7.26928 7.32534 7.20507 7.37089C7.02311 7.5 6.76541 7.5 6.25 7.5C5.73459 7.5 5.47689 7.5 5.29493 7.37089C5.23072 7.32534 5.17466 7.26928 5.12911 7.20507C5 7.02311 5 6.76541 5 6.25Z" fill="#1C274C"/>
|
||||
<path d="M5 17.75C5 17.2346 5 16.9769 5.12911 16.7949C5.17466 16.7307 5.23072 16.6747 5.29493 16.6291C5.47689 16.5 5.73459 16.5 6.25 16.5C6.76541 16.5 7.02311 16.5 7.20507 16.6291C7.26928 16.6747 7.32534 16.7307 7.37089 16.7949C7.5 16.9769 7.5 17.2346 7.5 17.75C7.5 18.2654 7.5 18.5231 7.37089 18.7051C7.32534 18.7693 7.26928 18.8253 7.20507 18.8709C7.02311 19 6.76541 19 6.25 19C5.73459 19 5.47689 19 5.29493 18.8709C5.23072 18.8253 5.17466 18.7693 5.12911 18.7051C5 18.5231 5 18.2654 5 17.75Z" fill="#1C274C"/>
|
||||
<path d="M16 17.75C16 17.0478 16 16.6967 16.1685 16.4444C16.2415 16.3352 16.3352 16.2415 16.4444 16.1685C16.6967 16 17.0478 16 17.75 16C18.4522 16 18.8033 16 19.0556 16.1685C19.1648 16.2415 19.2585 16.3352 19.3315 16.4444C19.5 16.6967 19.5 17.0478 19.5 17.75C19.5 18.4522 19.5 18.8033 19.3315 19.0556C19.2585 19.1648 19.1648 19.2585 19.0556 19.3315C18.8033 19.5 18.4522 19.5 17.75 19.5C17.0478 19.5 16.6967 19.5 16.4444 19.3315C16.3352 19.2585 16.2415 19.1648 16.1685 19.0556C16 18.8033 16 18.4522 16 17.75Z" fill="#1C274C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.8114 6.7267C12.8247 4.9089 13.3314 4 14.0889 4C14.8464 4 15.353 4.9089 16.3663 6.7267L16.6285 7.19699C16.9164 7.71355 17.0604 7.97183 17.2849 8.14225C17.5094 8.31266 17.789 8.37592 18.3482 8.50244L18.8572 8.61762C20.825 9.06284 21.8089 9.28545 22.0429 10.0382C22.277 10.7909 21.6063 11.5753 20.2648 13.1439L19.9177 13.5498C19.5365 13.9955 19.3459 14.2184 19.2602 14.4942C19.1744 14.7699 19.2032 15.0673 19.2609 15.662L19.3134 16.2035C19.5162 18.2965 19.6176 19.343 19.0047 19.8082C18.3919 20.2734 17.4707 19.8492 15.6283 19.0009L15.1517 18.7815C14.6281 18.5404 14.3664 18.4199 14.0889 18.4199C13.8114 18.4199 13.5496 18.5404 13.0261 18.7815L12.5494 19.0009C10.707 19.8492 9.78581 20.2734 9.17299 19.8082C8.56016 19.343 8.66157 18.2965 8.86438 16.2035L8.91685 15.662C8.97449 15.0673 9.0033 14.7699 8.91756 14.4942C8.83181 14.2184 8.64121 13.9955 8.26 13.5498L7.91295 13.1439C6.57147 11.5753 5.90073 10.7909 6.1348 10.0382C6.36888 9.28545 7.35275 9.06284 9.3205 8.61762L9.82958 8.50244C10.3887 8.37592 10.6683 8.31266 10.8928 8.14225C11.1173 7.97183 11.2613 7.71355 11.5492 7.19699L11.8114 6.7267Z" stroke="#1C274C" stroke-width="1.5"/>
|
||||
<path d="M2.08887 16C3.20445 15.121 4.68639 14.7971 6.08887 15.1257" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M2.08887 10.5C3.08887 10 3.37862 10.0605 4.08887 10" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M2 5.60867L2.20816 5.48676C4.41383 4.19506 6.75032 3.84687 8.95304 4.48161L9.16092 4.54152" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 8H10" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20.8333 9H18.2308C16.4465 9 15 10.3431 15 12C15 13.6569 16.4465 15 18.2308 15H20.8333C20.9167 15 20.9583 15 20.9935 14.9979C21.5328 14.965 21.9623 14.5662 21.9977 14.0654C22 14.0327 22 13.994 22 13.9167V10.0833C22 10.006 22 9.96726 21.9977 9.9346C21.9623 9.43384 21.5328 9.03496 20.9935 9.00214C20.9583 9 20.9167 9 20.8333 9Z" stroke="#1C274C" stroke-width="1.5"/>
|
||||
<path d="M20.965 9C20.8873 7.1277 20.6366 5.97975 19.8284 5.17157C18.6569 4 16.7712 4 13 4L10 4C6.22876 4 4.34315 4 3.17157 5.17157C2 6.34315 2 8.22876 2 12C2 15.7712 2 17.6569 3.17157 18.8284C4.34315 20 6.22876 20 10 20H13C16.7712 20 18.6569 20 19.8284 18.8284C20.6366 18.0203 20.8873 16.8723 20.965 15" stroke="#1C274C" stroke-width="1.5"/>
|
||||
<path d="M17.9912 12H18.0002" stroke="#1C274C" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -20,7 +20,7 @@
|
||||
if (popover) {
|
||||
const {x, y, width, height} = popover.popper.getBoundingClientRect()
|
||||
|
||||
if (!between([x, x + width], clientX) || !between([y - 30, y + height + 30], clientY)) {
|
||||
if (!between([x, x + width], clientX) || !between([y - 100, y + height + 100], clientY)) {
|
||||
popover.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
import Pallete2 from "@assets/icons/Pallete 2.svg?dataurl"
|
||||
import Paperclip from "@assets/icons/Paperclip.svg?dataurl"
|
||||
import Plain from "@assets/icons/Plain.svg?dataurl"
|
||||
import QRCode from "@assets/icons/QR Code.svg?dataurl"
|
||||
import QuestionSquare from "@assets/icons/Question Square.svg?dataurl"
|
||||
import RemoteControllerMinimalistic from "@assets/icons/Remote Controller Minimalistic.svg?dataurl"
|
||||
import Rocket2 from "@assets/icons/Rocket 2.svg?dataurl"
|
||||
@@ -85,11 +86,13 @@
|
||||
import SquareShareLine from "@assets/icons/Square Share Line.svg?dataurl"
|
||||
import SortVertical from "@assets/icons/Sort Vertical.svg?dataurl"
|
||||
import Star from "@assets/icons/Star.svg?dataurl"
|
||||
import StarFallMinimalistic2 from "@assets/icons/Star Fall Minimalistic 2.svg?dataurl"
|
||||
import TrashBin2 from "@assets/icons/Trash Bin 2.svg?dataurl"
|
||||
import UFO3 from "@assets/icons/UFO 3.svg?dataurl"
|
||||
import UserHeart from "@assets/icons/User Heart.svg?dataurl"
|
||||
import UserCircle from "@assets/icons/User Circle.svg?dataurl"
|
||||
import UserRounded from "@assets/icons/User Rounded.svg?dataurl"
|
||||
import Wallet from "@assets/icons/Wallet.svg?dataurl"
|
||||
import Widget from "@assets/icons/Widget.svg?dataurl"
|
||||
import WidgetAdd from "@assets/icons/Widget Add.svg?dataurl"
|
||||
import WiFiRouterRound from "@assets/icons/Wi-Fi Router Round.svg?dataurl"
|
||||
@@ -168,6 +171,7 @@
|
||||
"pallete-2": Pallete2,
|
||||
paperclip: Paperclip,
|
||||
plain: Plain,
|
||||
"qr-code": QRCode,
|
||||
"question-square": QuestionSquare,
|
||||
reply: Reply,
|
||||
"remote-controller-minimalistic": RemoteControllerMinimalistic,
|
||||
@@ -187,9 +191,11 @@
|
||||
"square-share-line": SquareShareLine,
|
||||
"sort-vertical": SortVertical,
|
||||
star: Star,
|
||||
"star-fall-minimalistic-2": StarFallMinimalistic2,
|
||||
"user-heart": UserHeart,
|
||||
"user-circle": UserCircle,
|
||||
"user-rounded": UserRounded,
|
||||
wallet: Wallet,
|
||||
widget: Widget,
|
||||
"widget-add": WidgetAdd,
|
||||
"wifi-router-round": WiFiRouterRound,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import QrScanner from "qr-scanner"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
|
||||
const {onscan} = $props()
|
||||
|
||||
let video: HTMLVideoElement
|
||||
let scanner: QrScanner
|
||||
let loading = $state(true)
|
||||
|
||||
onMount(() => {
|
||||
scanner = new QrScanner(video, r => onscan(r.data), {
|
||||
returnDetailedScanResult: true,
|
||||
})
|
||||
|
||||
scanner.start().then(() => {
|
||||
loading = false
|
||||
})
|
||||
|
||||
return () => scanner.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="bg-alt flex min-h-48 w-full flex-col items-center justify-center rounded p-px">
|
||||
{#if loading}
|
||||
<p class="py-20">
|
||||
<Spinner loading>Loading your camera...</Spinner>
|
||||
</p>
|
||||
{/if}
|
||||
<video class="m-auto rounded" class:h-0={loading} bind:this={video}></video>
|
||||
</div>
|
||||
@@ -7,6 +7,7 @@
|
||||
import {App} from "@capacitor/app"
|
||||
import {dev} from "$app/environment"
|
||||
import {goto} from "$app/navigation"
|
||||
import {sync, localStorageProvider} from "@welshman/store"
|
||||
import {identity, memoize, sleep, defer, ago, WEEK, TaskQueue} from "@welshman/lib"
|
||||
import type {TrustedEvent, StampedEvent} from "@welshman/util"
|
||||
import {
|
||||
@@ -33,14 +34,16 @@
|
||||
initStorage,
|
||||
repository,
|
||||
pubkey,
|
||||
defaultStorageAdapters,
|
||||
session,
|
||||
sessions,
|
||||
signer,
|
||||
dropSession,
|
||||
defaultStorageAdapters,
|
||||
userInboxRelaySelections,
|
||||
loginWithNip01,
|
||||
loginWithNip46,
|
||||
EventsStorageAdapter,
|
||||
loadRelaySelections,
|
||||
} from "@welshman/app"
|
||||
import * as lib from "@welshman/lib"
|
||||
import * as util from "@welshman/util"
|
||||
@@ -168,17 +171,29 @@
|
||||
const unwrapper = new TaskQueue<TrustedEvent>({batchSize: 10, processItem: ensureUnwrapped})
|
||||
|
||||
repository.on("update", ({added}) => {
|
||||
if (!$canDecrypt) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const event of added) {
|
||||
if (event.kind === WRAP) {
|
||||
loadRelaySelections(event.pubkey)
|
||||
|
||||
if ($canDecrypt && event.kind === WRAP) {
|
||||
unwrapper.push(event)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Sync current pubkey
|
||||
sync({
|
||||
key: "pubkey",
|
||||
store: pubkey,
|
||||
storage: localStorageProvider,
|
||||
})
|
||||
|
||||
// Sync user sessions
|
||||
sync({
|
||||
key: "sessions",
|
||||
store: sessions,
|
||||
storage: localStorageProvider,
|
||||
})
|
||||
|
||||
await initStorage("flotilla", 8, {
|
||||
...defaultStorageAdapters,
|
||||
events: new EventsStorageAdapter({
|
||||
|
||||
@@ -28,6 +28,11 @@
|
||||
<Icon icon="user-circle" /> Profile
|
||||
</SecondaryNavItem>
|
||||
</div>
|
||||
<div in:fly|local>
|
||||
<SecondaryNavItem href="/settings/wallet">
|
||||
<Icon icon="wallet" /> Wallet
|
||||
</SecondaryNavItem>
|
||||
</div>
|
||||
<div in:fly|local={{delay: 50}}>
|
||||
<SecondaryNavItem href="/settings/relays">
|
||||
<Icon icon="server" /> Relays
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
import {pushModal} from "@app/modal"
|
||||
import {clip} from "@app/toast"
|
||||
|
||||
const npub = nip19.npubEncode($pubkey!)
|
||||
const profile = deriveProfile($pubkey!)
|
||||
|
||||
const pubkeyDisplay = displayPubkey($pubkey!)
|
||||
|
||||
const copyNpub = () => clip(nip19.npubEncode($session!.pubkey))
|
||||
const copyNpub = () => clip(npub)
|
||||
|
||||
const copyNsec = () => clip(nip19.nsecEncode(hexToBytes($session!.secret!)))
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<label class="input input-bordered flex w-full items-center justify-between gap-2">
|
||||
<div class="row-2 flex-grow items-center">
|
||||
<Icon icon="link-round" />
|
||||
<input readonly class="ellipsize flex-grow" value={$session?.pubkey} />
|
||||
<input readonly class="ellipsize flex-grow" value={npub} />
|
||||
</div>
|
||||
<Button class="flex items-center" onclick={copyNpub}>
|
||||
<Icon icon="copy" />
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<script lang="ts">
|
||||
import {nwc} from "@getalby/sdk"
|
||||
import {LOCALE} from "@welshman/lib"
|
||||
import {displayRelayUrl, fromMsats} from "@welshman/util"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import WalletConnect from "@app/components/WalletConnect.svelte"
|
||||
import WalletDisconnect from "@app/components/WalletDisconnect.svelte"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {wallet, getWebLn} from "@app/state"
|
||||
|
||||
const connect = () => pushModal(WalletConnect)
|
||||
|
||||
const disconnect = () => pushModal(WalletDisconnect)
|
||||
</script>
|
||||
|
||||
<div class="content column gap-4">
|
||||
<div class="card2 bg-alt flex flex-col gap-6 shadow-xl">
|
||||
<div class="flex items-center justify-between">
|
||||
<strong class="flex items-center gap-3">
|
||||
<Icon icon="wallet" />
|
||||
Wallet
|
||||
</strong>
|
||||
{#if $wallet}
|
||||
<div class="flex items-center gap-2 text-sm text-success">
|
||||
<Icon icon="check-circle" size={4} />
|
||||
Connected
|
||||
</div>
|
||||
{:else}
|
||||
<Button class="btn btn-primary btn-sm" onclick={connect}>
|
||||
<Icon icon="add-circle" />
|
||||
Connect Wallet
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-4">
|
||||
{#if $wallet}
|
||||
{#if $wallet?.type === "webln"}
|
||||
{@const {node, version} = $wallet.info}
|
||||
<div class="flex flex-col justify-between gap-2 lg:flex-row">
|
||||
<p>
|
||||
Connected to <strong>{node?.alias || version || "unknown wallet"}</strong>
|
||||
via <strong>{$wallet.type}</strong>
|
||||
</p>
|
||||
<p class="flex gap-2 whitespace-nowrap">
|
||||
Balance:
|
||||
{#await getWebLn()
|
||||
?.enable()
|
||||
.then(() => getWebLn().getBalance())}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{:then res}
|
||||
{new Intl.NumberFormat(LOCALE).format(res?.balance || 0)}
|
||||
{:catch}
|
||||
[unknown]
|
||||
{/await}
|
||||
sats
|
||||
</p>
|
||||
</div>
|
||||
{:else if $wallet.type === "nwc"}
|
||||
{@const {lud16, relayUrl, nostrWalletConnectUrl} = $wallet.info}
|
||||
<div class="flex flex-col justify-between gap-2 lg:flex-row">
|
||||
<p>
|
||||
Connected to <strong>{lud16}</strong> via <strong>{displayRelayUrl(relayUrl)}</strong>
|
||||
</p>
|
||||
<p class="flex gap-2 whitespace-nowrap">
|
||||
Balance:
|
||||
{#await new nwc.NWCClient({nostrWalletConnectUrl}).getBalance()}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{:then res}
|
||||
{new Intl.NumberFormat(LOCALE).format(fromMsats(res?.balance || 0))}
|
||||
{:catch}
|
||||
[unknown]
|
||||
{/await}
|
||||
sats
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
<Button class="btn btn-neutral btn-sm" onclick={disconnect}>
|
||||
<Icon icon="close-circle" />
|
||||
Disconnect Wallet
|
||||
</Button>
|
||||
{:else}
|
||||
<p class="py-12 text-center opacity-75">No wallet connected</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,7 +62,7 @@
|
||||
relays,
|
||||
filters: [
|
||||
{kinds: [ROOM_META]},
|
||||
{kinds: [THREAD, EVENT_TIME], since},
|
||||
{kinds: [THREAD, EVENT_TIME, MESSAGE], since},
|
||||
{kinds: [COMMENT], "#K": [String(THREAD), String(EVENT_TIME)], since},
|
||||
...rooms.map(room => ({kinds: [MESSAGE], "#h": [room], since})),
|
||||
],
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
makeRoomMeta,
|
||||
MESSAGE,
|
||||
DELETE,
|
||||
REACTION,
|
||||
ROOM_ADD_USER,
|
||||
ROOM_REMOVE_USER,
|
||||
} from "@welshman/util"
|
||||
@@ -37,6 +36,7 @@
|
||||
deriveUserMembershipStatus,
|
||||
deriveChannel,
|
||||
MembershipStatus,
|
||||
REACTION_KINDS,
|
||||
} from "@app/state"
|
||||
import {setChecked, checked} from "@app/notifications"
|
||||
import {addRoomMembership, removeRoomMembership, prependParent} from "@app/commands"
|
||||
@@ -226,7 +226,9 @@
|
||||
element: element!,
|
||||
relays: [url],
|
||||
feedFilters: [filter],
|
||||
subscriptionFilters: [{kinds: [DELETE, REACTION, MESSAGE], "#h": [room], since: now()}],
|
||||
subscriptionFilters: [
|
||||
{kinds: [DELETE, MESSAGE, ...REACTION_KINDS], "#h": [room], since: now()},
|
||||
],
|
||||
initialEvents: getEventsForUrl(url, [{...filter, limit: 20}]),
|
||||
onExhausted: () => {
|
||||
loadingEvents = false
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import {page} from "$app/stores"
|
||||
import {now, last, formatTimestampAsDate} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {REACTION, DELETE, EVENT_TIME, getTagValue} from "@welshman/util"
|
||||
import {DELETE, EVENT_TIME, getTagValue} from "@welshman/util"
|
||||
import {fly} from "@lib/transition"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -17,7 +17,7 @@
|
||||
import CalendarEventItem from "@app/components/CalendarEventItem.svelte"
|
||||
import CalendarEventCreate from "@app/components/CalendarEventCreate.svelte"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {getEventsForUrl, decodeRelay} from "@app/state"
|
||||
import {getEventsForUrl, decodeRelay, REACTION_KINDS} from "@app/state"
|
||||
import {makeCalendarFeed} from "@app/requests"
|
||||
import {setChecked} from "@app/notifications"
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
onMount(() => {
|
||||
const feedFilters = [{kinds: [EVENT_TIME]}]
|
||||
const subscriptionFilters = [{kinds: [DELETE, REACTION, EVENT_TIME], since: now()}]
|
||||
const subscriptionFilters = [{kinds: [DELETE, EVENT_TIME, ...REACTION_KINDS], since: now()}]
|
||||
|
||||
;({events, cleanup} = makeCalendarFeed({
|
||||
element: element!,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import type {Readable} from "svelte/store"
|
||||
import {now, formatTimestampAsDate} from "@welshman/lib"
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {makeEvent, MESSAGE, DELETE, REACTION} from "@welshman/util"
|
||||
import {makeEvent, MESSAGE, DELETE} from "@welshman/util"
|
||||
import {pubkey, publishThunk} from "@welshman/app"
|
||||
import {slide, fade, fly} from "@lib/transition"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
@@ -21,7 +21,7 @@
|
||||
import {userSettingValues, decodeRelay, getEventsForUrl} from "@app/state"
|
||||
import {setChecked, checked} from "@app/notifications"
|
||||
import {prependParent} from "@app/commands"
|
||||
import {PROTECTED} from "@app/state"
|
||||
import {PROTECTED, REACTION_KINDS} from "@app/state"
|
||||
import {makeFeed} from "@app/requests"
|
||||
import {popKey} from "@app/implicit"
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
element: element!,
|
||||
relays: [url],
|
||||
feedFilters: [filter],
|
||||
subscriptionFilters: [{kinds: [DELETE, REACTION, MESSAGE], since: now()}],
|
||||
subscriptionFilters: [{kinds: [DELETE, MESSAGE, ...REACTION_KINDS], since: now()}],
|
||||
initialEvents: getEventsForUrl(url, [{...filter, limit: 20}]),
|
||||
onExhausted: () => {
|
||||
loadingEvents = false
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {page} from "$app/stores"
|
||||
import {sortBy, max, nthEq} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {ZAP_GOAL, DELETE, COMMENT, getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||
import {userMutes} from "@welshman/app"
|
||||
import {fly} from "@lib/transition"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import PageBar from "@lib/components/PageBar.svelte"
|
||||
import PageContent from "@lib/components/PageContent.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import MenuSpaceButton from "@app/components/MenuSpaceButton.svelte"
|
||||
import GoalItem from "@app/components/GoalItem.svelte"
|
||||
import GoalCreate from "@app/components/GoalCreate.svelte"
|
||||
import {decodeRelay, getEventsForUrl, REACTION_KINDS} from "@app/state"
|
||||
import {setChecked} from "@app/notifications"
|
||||
import {makeFeed} from "@app/requests"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
const url = decodeRelay($page.params.relay)
|
||||
const mutedPubkeys = getPubkeyTagValues(getListTags($userMutes))
|
||||
const goals: TrustedEvent[] = $state([])
|
||||
const comments: TrustedEvent[] = $state([])
|
||||
|
||||
let loading = $state(true)
|
||||
let element: HTMLElement | undefined = $state()
|
||||
|
||||
const createGoal = () => pushModal(GoalCreate, {url})
|
||||
|
||||
const events = $derived.by(() => {
|
||||
const scores = new Map<string, number>()
|
||||
|
||||
for (const comment of comments) {
|
||||
const id = comment.tags.find(nthEq(0, "E"))?.[1]
|
||||
|
||||
if (id) {
|
||||
scores.set(id, max([scores.get(id), comment.created_at]))
|
||||
}
|
||||
}
|
||||
|
||||
return sortBy(e => -max([scores.get(e.id), e.created_at]), goals)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
const {cleanup} = makeFeed({
|
||||
element: element!,
|
||||
relays: [url],
|
||||
feedFilters: [{kinds: [ZAP_GOAL, COMMENT]}],
|
||||
subscriptionFilters: [
|
||||
{kinds: [ZAP_GOAL, DELETE, ...REACTION_KINDS]},
|
||||
{kinds: [COMMENT], "#K": [String(ZAP_GOAL)]},
|
||||
],
|
||||
initialEvents: getEventsForUrl(url, [{kinds: [ZAP_GOAL, COMMENT], limit: 10}]),
|
||||
onEvent: event => {
|
||||
if (event.kind === ZAP_GOAL && !mutedPubkeys.includes(event.pubkey)) {
|
||||
goals.push(event)
|
||||
}
|
||||
|
||||
if (event.kind === COMMENT) {
|
||||
comments.push(event)
|
||||
}
|
||||
},
|
||||
onExhausted: () => {
|
||||
loading = false
|
||||
},
|
||||
})
|
||||
|
||||
return () => {
|
||||
cleanup()
|
||||
setChecked($page.url.pathname)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<PageBar>
|
||||
{#snippet icon()}
|
||||
<div class="center">
|
||||
<Icon icon="notes-minimalistic" />
|
||||
</div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<strong>Goals</strong>
|
||||
{/snippet}
|
||||
{#snippet action()}
|
||||
<div class="row-2">
|
||||
<Button class="btn btn-primary btn-sm" onclick={createGoal}>
|
||||
<Icon icon="notes-minimalistic" />
|
||||
Create a Goal
|
||||
</Button>
|
||||
<MenuSpaceButton {url} />
|
||||
</div>
|
||||
{/snippet}
|
||||
</PageBar>
|
||||
|
||||
<PageContent bind:element class="flex flex-col gap-2 p-2 pt-4">
|
||||
{#each events as event (event.id)}
|
||||
<div in:fly>
|
||||
<GoalItem {url} event={$state.snapshot(event)} />
|
||||
</div>
|
||||
{/each}
|
||||
<p class="flex h-10 items-center justify-center py-20">
|
||||
<Spinner {loading}>
|
||||
{#if loading}
|
||||
Looking for goals...
|
||||
{:else if events.length === 0}
|
||||
No goals found.
|
||||
{:else}
|
||||
That's all!
|
||||
{/if}
|
||||
</Spinner>
|
||||
</p>
|
||||
</PageContent>
|
||||
@@ -0,0 +1,123 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {page} from "$app/stores"
|
||||
import {sortBy, sleep} from "@welshman/lib"
|
||||
import {COMMENT, getTagValue} from "@welshman/util"
|
||||
import {repository} from "@welshman/app"
|
||||
import {request} from "@welshman/net"
|
||||
import {deriveEvents} from "@welshman/store"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import PageBar from "@lib/components/PageBar.svelte"
|
||||
import PageContent from "@lib/components/PageContent.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Content from "@app/components/Content.svelte"
|
||||
import NoteCard from "@app/components/NoteCard.svelte"
|
||||
import MenuSpaceButton from "@app/components/MenuSpaceButton.svelte"
|
||||
import GoalSummary from "@app/components/GoalSummary.svelte"
|
||||
import GoalActions from "@app/components/GoalActions.svelte"
|
||||
import CommentActions from "@app/components/CommentActions.svelte"
|
||||
import EventReply from "@app/components/EventReply.svelte"
|
||||
import {deriveEvent, decodeRelay} from "@app/state"
|
||||
import {setChecked} from "@app/notifications"
|
||||
|
||||
const {relay, id} = $page.params
|
||||
const url = decodeRelay(relay)
|
||||
const event = deriveEvent(id)
|
||||
const filters = [{kinds: [COMMENT], "#E": [id]}]
|
||||
const replies = deriveEvents(repository, {filters})
|
||||
const summary = getTagValue("summary", $event.tags)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const openReply = () => {
|
||||
showReply = true
|
||||
}
|
||||
|
||||
const closeReply = () => {
|
||||
showReply = false
|
||||
}
|
||||
|
||||
const expand = () => {
|
||||
showAll = true
|
||||
}
|
||||
|
||||
let showAll = $state(false)
|
||||
let showReply = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
const controller = new AbortController()
|
||||
|
||||
request({relays: [url], filters, signal: controller.signal})
|
||||
|
||||
return () => {
|
||||
controller.abort()
|
||||
setChecked($page.url.pathname)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<PageBar>
|
||||
{#snippet icon()}
|
||||
<div>
|
||||
<Button class="btn btn-neutral btn-sm flex-nowrap whitespace-nowrap" onclick={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
<span class="hidden sm:inline">Go back</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<h1 class="text-xl">{$event.content}</h1>
|
||||
{/snippet}
|
||||
{#snippet action()}
|
||||
<div>
|
||||
<MenuSpaceButton {url} />
|
||||
</div>
|
||||
{/snippet}
|
||||
</PageBar>
|
||||
|
||||
<PageContent class="flex flex-col p-2 pt-4">
|
||||
{#if $event}
|
||||
<div class="flex flex-col gap-3">
|
||||
<NoteCard event={$event} {url} class="card2 bg-alt z-feature w-full">
|
||||
<div class="col-3 ml-12">
|
||||
<Content showEntire event={{...$event, content: summary}} {url} />
|
||||
<GoalSummary event={$event} {url} />
|
||||
<GoalActions event={$event} {url} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{#if !showAll && $replies.length > 4}
|
||||
<div class="flex justify-center">
|
||||
<Button class="btn btn-link" onclick={expand}>
|
||||
<Icon icon="sort-vertical" />
|
||||
Show all {$replies.length} replies
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#each sortBy(e => e.created_at, $replies).slice(0, showAll ? undefined : 4) as reply (reply.id)}
|
||||
<NoteCard event={reply} {url} class="card2 bg-alt z-feature w-full">
|
||||
<div class="col-3 ml-12">
|
||||
<Content showEntire event={reply} {url} />
|
||||
<CommentActions event={reply} {url} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{/each}
|
||||
</div>
|
||||
{#if showReply}
|
||||
<EventReply {url} event={$event} onClose={closeReply} onSubmit={closeReply} />
|
||||
{:else}
|
||||
<div class="flex justify-end p-2">
|
||||
<Button class="btn btn-primary" onclick={openReply}>
|
||||
<Icon icon="reply" />
|
||||
Comment on this goal
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
{#await sleep(5000)}
|
||||
<Spinner loading>Loading funding goal...</Spinner>
|
||||
{:then}
|
||||
<p>Failed to load funding goal.</p>
|
||||
{/await}
|
||||
{/if}
|
||||
</PageContent>
|
||||
@@ -3,7 +3,7 @@
|
||||
import {page} from "$app/stores"
|
||||
import {sortBy, max, nthEq} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {THREAD, REACTION, DELETE, COMMENT, getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||
import {THREAD, DELETE, COMMENT, getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||
import {userMutes} from "@welshman/app"
|
||||
import {fly} from "@lib/transition"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
@@ -16,6 +16,7 @@
|
||||
import ThreadCreate from "@app/components/ThreadCreate.svelte"
|
||||
import {decodeRelay, getEventsForUrl} from "@app/state"
|
||||
import {setChecked} from "@app/notifications"
|
||||
import {REACTION_KINDS} from "@app/state"
|
||||
import {makeFeed} from "@app/requests"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
@@ -49,7 +50,7 @@
|
||||
relays: [url],
|
||||
feedFilters: [{kinds: [THREAD, COMMENT]}],
|
||||
subscriptionFilters: [
|
||||
{kinds: [THREAD, REACTION, DELETE]},
|
||||
{kinds: [THREAD, DELETE, ...REACTION_KINDS]},
|
||||
{kinds: [COMMENT], "#K": [String(THREAD)]},
|
||||
],
|
||||
initialEvents: getEventsForUrl(url, [{kinds: [THREAD, COMMENT], limit: 10}]),
|
||||
|
||||
@@ -25,4 +25,9 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
compilerOptions: {
|
||||
warningFilter: (warning) => {
|
||||
return !['a11y_media_has_caption'].includes(warning.code)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+12
-8
@@ -1,9 +1,13 @@
|
||||
flotilla:
|
||||
android:
|
||||
identifier: social.flotilla
|
||||
name: Flotilla
|
||||
description: Self-hosted community chat and threads built on the nostr protocol.
|
||||
repository: https://github.com/coracle-social/flotilla
|
||||
artifacts:
|
||||
- app-release-signed.apk
|
||||
identifier: social.flotilla
|
||||
name: Flotilla
|
||||
tags: nostr nip29 community chat group
|
||||
changelog: CHANGELOG.md
|
||||
homepage: https://flotilla.social
|
||||
description: Self-hosted community chat and threads built on the nostr protocol.
|
||||
repository: https://github.com/coracle-social/flotilla
|
||||
blossom_servers:
|
||||
- https://cdn.zapstore.dev
|
||||
- https://hbr.coracle.social
|
||||
assets:
|
||||
- app-release-signed.apk
|
||||
|
||||
|
||||
Reference in New Issue
Block a user