Compare commits

...

6 Commits

Author SHA1 Message Date
mplorentz 90c2cc67c0 Move handleJoinerror into VoiceRoomJoinDialog. 2026-04-01 16:45:24 -04:00
mplorentz e7fd75e06e Fix error toast when failing to join room. 2026-04-01 16:38:46 -04:00
Jon Staab 823a9c3271 Combine discover and space list into a single page 2026-03-31 14:24:09 -07:00
Jon Staab fe89df2aa3 Fix some chat related bugs 2026-03-31 11:25:59 -07:00
Jon Staab 97ff8ff802 Bump version 2026-03-31 09:55:04 -07:00
Jon Staab a10a9e7043 Bump pomade 2026-03-31 09:53:30 -07:00
21 changed files with 286 additions and 312 deletions
+1
View File
@@ -15,6 +15,7 @@ VITE_PUSH_BRIDGE=wss://npb.coracle.social/
VITE_BLOCKED_RELAYS=brb.io,relay.nostr.band,nostr.mutinywallet.com,feeds.nostr.band,nostr.zbd.gg,wot.utxo.one,blastr.f7z.xyz,relay.current.fyi
VITE_INDEXER_RELAYS=purplepag.es,relay.damus.io,indexer.coracle.social
VITE_DEFAULT_RELAYS=relay.damus.io,relay.primal.net,nostr.mom
VITE_DEFAULT_SEARCH_RELAYS=relay.ditto.pub,antiprimal.net,relay.vertexlab.io
VITE_DEFAULT_MESSAGING_RELAYS=auth.nostr1.com,relay.keychat.io,relay.ditto.pub
VITE_SIGNER_RELAYS=relay.nsec.app,ephemeral.snowflare.cc,bucket.coracle.social
VITE_VAPID_PUBLIC_KEY=BIt2D4BdgdbCowD_0d3Np6GbrIGHxd7aIEUeZNe3hQuRlHz02OhzvDaai0XSFoJYVzSzdMjdyW-QhvW9_yq8j4Y
+4
View File
@@ -1,5 +1,9 @@
# Changelog
# 1.7.1
* Fix pomade registration fallback in case of offline signer
# 1.7.0
* Enable email/password login
+2 -2
View File
@@ -8,8 +8,8 @@ android {
applicationId "social.flotilla"
minSdk rootProject.ext.minSdkVersion
targetSdk rootProject.ext.targetSdkVersion
versionCode 42
versionName "1.7.0"
versionCode 43
versionName "1.7.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+4 -4
View File
@@ -358,14 +358,14 @@
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 33;
CURRENT_PROJECT_VERSION = 34;
DEVELOPMENT_TEAM = S26U9DYW3A;
INFOPLIST_FILE = App/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.7.0;
MARKETING_VERSION = 1.7.1;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -385,14 +385,14 @@
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 33;
CURRENT_PROJECT_VERSION = 34;
DEVELOPMENT_TEAM = S26U9DYW3A;
INFOPLIST_FILE = App/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.7.0;
MARKETING_VERSION = 1.7.1;
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "flotilla",
"version": "1.7.0",
"version": "1.7.1",
"private": true,
"scripts": {
"dev": "vite dev",
@@ -58,7 +58,7 @@
"@getalby/lightning-tools": "^6.1.0",
"@getalby/sdk": "^5.1.2",
"@noble/curves": "^1.9.7",
"@pomade/core": "^0.2.1",
"@pomade/core": "^0.2.2",
"@poppanator/sveltekit-svg": "^4.2.1",
"@sveltejs/adapter-static": "^3.0.10",
"@tiptap/core": "^2.27.2",
+43 -9
View File
@@ -60,8 +60,8 @@ importers:
specifier: ^1.9.7
version: 1.9.7
'@pomade/core':
specifier: ^0.2.1
version: 0.2.1(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.10(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
specifier: ^0.2.2
version: 0.2.2(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.10(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
'@poppanator/sveltekit-svg':
specifier: ^4.2.1
version: 4.2.1(rollup@2.80.0)(svelte@5.48.0)(svgo@3.3.2)(vite@5.4.21(@types/node@25.0.10)(terser@5.46.0))
@@ -85,7 +85,7 @@ importers:
version: 0.6.8(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.48.0)(vite@5.4.21(@types/node@25.0.10)(terser@5.46.0)))(svelte@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@25.0.10)(terser@5.46.0)))(@vite-pwa/assets-generator@0.2.6)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@0.2.6)(vite@5.4.21(@types/node@25.0.10)(terser@5.46.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
'@welshman/app':
specifier: ^0.8.10
version: 0.8.10(b1057552692475ccd3b973b40142e1b2)
version: 0.8.10(f8bcfd183423c537ab4935d047335c0f)
'@welshman/content':
specifier: ^0.8.10
version: 0.8.10(nostr-tools@2.20.0(typescript@5.9.3))
@@ -1124,89 +1124,105 @@ packages:
resolution: {integrity: sha512-HWpu3wRqss0vqze56Y/peCrMOsILjoorwU0ZiqF4dYQIl03dD4k71tHstC2/y+7KqNtgb7+ItSdXJydfwspDyA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.3.0-rc.2':
resolution: {integrity: sha512-tyXAQ0WCfXZf2dwm7F+IN/t/s324EcdpbW3dh8rwh8NHIkijeHGyiAHs45Bs8SnsTM/RjR+uPigxFMF/QYAiTw==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.3.0-rc.2':
resolution: {integrity: sha512-lfJrlawp2PjxBu3Nh/2EOsgigNgr2o8MOG3XS2ibkKpJ3K/1YcUu9sTQV0S/n8+ak2R9MmJ3uTJqRVjdYkwWxg==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.3.0-rc.2':
resolution: {integrity: sha512-2WUcL/k7uk6i5ZSXCQmOGgGxwsfKEtJA28vNfFb6gT+Zv7vdAMbnjjGMFRtTT7RUcFHgN2olvhfnEjtIMY49MQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.3.0-rc.2':
resolution: {integrity: sha512-wx8/WmVA+kwLfwEN6UzjFlRz6erSibq4nGItfH3Nv+OITCjx8pH3Sl67T0tbwjU3M24GOcDFBIJ6rB+2oXbMzQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.3.0-rc.2':
resolution: {integrity: sha512-Rk8oOssrmTzhiuhDMPCw5Gadd4/mj5QPqrkxbg0R1VEaQeNo51d4YNbIokEDp2PqSTo+unUs6SHN8prkFKHP5A==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.3.0-rc.2':
resolution: {integrity: sha512-K2TaPlrPox8uf3K01R8S+AfhPqVVWlEK6+RxkJNGasN0k1iFhu9hMhWl7+sEiWj2V30TKcppRqQWUv7H3qym9w==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.3.0-rc.2':
resolution: {integrity: sha512-88HtWiP7sBX6rb1Hw7cf3H+1ufkB+YocfeMFtmAg6oOc8hvNcby8MVPyE7GL+YD7LKPBadcScaeVnGzYWYRaMQ==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.35.0-rc.0':
resolution: {integrity: sha512-6pdCj+H0+sNsX7vpbxF2FhDF+fe7Hc/mfjlG5caFVUrACIMMOMc962xTE0Y1+XE3EWM8hUmGp3y6hISjJDnwYA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.35.0-rc.0':
resolution: {integrity: sha512-oy0+atKDov9vn9mMVlyS0V2BTKTFzR3cFhZ9ilF98vnGSvrWdevUlfjNdTE5pN/xZq6z0GnkIAUL5KJhwh5yzQ==}
engines: {node: '>=20.9.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.35.0-rc.0':
resolution: {integrity: sha512-SvX6E6h/i/zhhGheJiSHbFb3loglNDi+H6wEpnPdp5SGlyiJabLVco93kHBeo4lkfQAwYACRW+yFhRfNhhZO3Q==}
engines: {node: '>=20.9.0'}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.35.0-rc.0':
resolution: {integrity: sha512-EY+6k/u87hfZgBXJaNNRwEN1ACPHUudcW2ObVmNA1eADnFircVvdPjvKUCc5bVwzjmG1imzNJWoapIB9wZheFQ==}
engines: {node: '>=20.9.0'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.35.0-rc.0':
resolution: {integrity: sha512-owTWB3KiGs03QpcHFGNBToB5DIEuCsHugzVS7h8Za6f2W0Nw8qedJF5oSIr71nBj4jF4NmTMd5w7EFOOKn8pFA==}
engines: {node: '>=20.9.0'}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.35.0-rc.0':
resolution: {integrity: sha512-XQoXfEHwz0TTtf6DMT7rLXNc7qb7okjSe+8vzusdTuqVXBi+km+Jwvc9DKL9azMWKR4TR/ArvQ7Y5dFnckb9VA==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.35.0-rc.0':
resolution: {integrity: sha512-hfCXVq35g/zA+Lpa3x1gUXHLi0rLWUPbVGpbPox2zyx2byfKXf5Lcq6xoMJUrQkmT+s8BaOP8TbmhG8ZQgUFyw==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.35.0-rc.0':
resolution: {integrity: sha512-TsV3KFF9i2wKHHsXnRz9N5H66swuDqpk+KPiTugbtdTo+um33BoFZC7F+Ty+DBoOWf4TT+j7IiJfpen5bC5FRw==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.35.0-rc.0':
resolution: {integrity: sha512-5eRvTRqUbNDEd999tRRwXaEO5CERA1WDiVrNDgh+g0IlhCJ79jQkfTE+/dKEO8VbhUwVT6qWFjse+/3KjXhUKg==}
@@ -1408,9 +1424,9 @@ packages:
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@pomade/core@0.2.1':
resolution: {integrity: sha512-zXpPQPkhVe7OchmRDe2MbHdUxiCSeUuMwrHOyeOBs/xD1EfY093Mwj6Cu/OLfz0wxivBDSp1GMMmxqKbLWam3Q==}
version: 0.2.1
'@pomade/core@0.2.2':
resolution: {integrity: sha512-FoilLsO0gVjiKMW3LV63pmXU7x3gh8YVGVulyR6QJr4h47XrsBg8vPkZtKWr4+sH3sW31e2tNIPUb3ptiuhrMA==}
version: 0.2.2
engines: {node: '>=12.0.0'}
peerDependencies:
'@frostr/bifrost': ^1.0.7
@@ -1520,66 +1536,79 @@ packages:
resolution: {integrity: sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.56.0':
resolution: {integrity: sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.56.0':
resolution: {integrity: sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.56.0':
resolution: {integrity: sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.56.0':
resolution: {integrity: sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.56.0':
resolution: {integrity: sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.56.0':
resolution: {integrity: sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.56.0':
resolution: {integrity: sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.56.0':
resolution: {integrity: sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.56.0':
resolution: {integrity: sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.56.0':
resolution: {integrity: sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.56.0':
resolution: {integrity: sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.56.0':
resolution: {integrity: sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.56.0':
resolution: {integrity: sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==}
@@ -1702,30 +1731,35 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.10.0':
resolution: {integrity: sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.10.0':
resolution: {integrity: sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.10.0':
resolution: {integrity: sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.10.0':
resolution: {integrity: sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.10.0':
resolution: {integrity: sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==}
@@ -6536,7 +6570,7 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
'@pomade/core@0.2.1(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.10(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))':
'@pomade/core@0.2.2(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.10(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))':
dependencies:
'@frostr/bifrost': 1.0.7(typescript@5.9.3)
'@noble/hashes': 2.0.1
@@ -7133,9 +7167,9 @@ snapshots:
optionalDependencies:
'@vite-pwa/assets-generator': 0.2.6
'@welshman/app@0.8.10(b1057552692475ccd3b973b40142e1b2)':
'@welshman/app@0.8.10(f8bcfd183423c537ab4935d047335c0f)':
dependencies:
'@pomade/core': 0.2.1(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.10(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
'@pomade/core': 0.2.2(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.10(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.10)(@welshman/net@0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
'@welshman/feeds': 0.8.10(d287ec628e3b45481639b01eedf791d2)
'@welshman/lib': 0.8.10
'@welshman/net': 0.8.10(@welshman/lib@0.8.10)(@welshman/util@0.8.10(@noble/curves@1.9.7)(@welshman/lib@0.8.10)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
+2 -1
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import type {Snippet} from "svelte"
import {onMount} from "svelte"
import {goto} from "$app/navigation"
import {
ago,
int,
@@ -73,7 +74,7 @@
? pushModal(ProfileDetail, {pubkey: others[0]})
: pushModal(ChatMembers, {pubkeys: others})
const back = () => history.back()
const back = () => goto("/chat")
const replyTo = (event: TrustedEvent) => {
parent = event
+1 -1
View File
@@ -3,7 +3,7 @@
import {Capacitor} from "@capacitor/core"
import {getNip07, getNip55, Nip55Signer} from "@welshman/signer"
import {addSession, type Session, makeNip07Session, makeNip55Session} from "@welshman/app"
import Widget from "@assets/icons/widget-2.svg?dataurl"
import Widget from "@assets/icons/widget-4.svg?dataurl"
import Letter from "@assets/icons/letter.svg?dataurl"
import Cpu from "@assets/icons/cpu-bolt.svg?dataurl"
import Compass from "@assets/icons/compass-big.svg?dataurl"
-32
View File
@@ -1,32 +0,0 @@
<script lang="ts">
import Link from "@lib/components/Link.svelte"
import CardButton from "@lib/components/CardButton.svelte"
import RelayIcon from "@app/components/RelayIcon.svelte"
import RelayName from "@app/components/RelayName.svelte"
import RelayDescription from "@app/components/RelayDescription.svelte"
import {makeSpacePath} from "@app/util/routes"
import {notifications} from "@app/util/notifications"
const {url} = $props()
const path = makeSpacePath(url)
</script>
<Link replaceState href={path}>
<CardButton class="btn-neutral shadow-md bg-alt rounded-box border-none">
{#snippet icon()}
<RelayIcon {url} size={12} class="rounded-full" />
{/snippet}
{#snippet title()}
<div class="flex gap-1">
<RelayName {url} />
{#if $notifications.has(path)}
<div class="relative top-1 h-2 w-2 rounded-full bg-primary"></div>
{/if}
</div>
{/snippet}
{#snippet info()}
<div><RelayDescription {url} /></div>
{/snippet}
</CardButton>
</Link>
+2 -2
View File
@@ -3,7 +3,7 @@
import {userProfile} from "@welshman/app"
import Letter from "@assets/icons/letter.svg?dataurl"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
import Planet from "@assets/icons/planet-3.svg?dataurl"
import Widget from "@assets/icons/widget-4.svg?dataurl"
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
import Settings from "@assets/icons/settings.svg?dataurl"
import ImageIcon from "@lib/components/ImageIcon.svelte"
@@ -84,7 +84,7 @@
</PrimaryNavItem>
{#if PLATFORM_RELAYS.length !== 1}
<PrimaryNavItem title="Spaces" href="/spaces" notification={anySpaceNotifications}>
<ImageIcon alt="Spaces" src={Planet} size={8} />
<ImageIcon alt="Spaces" src={Widget} size={8} />
</PrimaryNavItem>
{/if}
</div>
+2 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import {splitAt} from "@welshman/lib"
import Widget from "@assets/icons/widget.svg?dataurl"
import Widget from "@assets/icons/widget-4.svg?dataurl"
import Compass from "@assets/icons/compass.svg?dataurl"
import ImageIcon from "@lib/components/ImageIcon.svelte"
import Divider from "@lib/components/Divider.svelte"
@@ -38,7 +38,7 @@
notification={otherSpaceNotifications}>
<ImageIcon alt="All Spaces" src={Widget} size={8} />
</PrimaryNavItem>
<PrimaryNavItem title="Add a Space" href="/discover" class="tooltip-right">
<PrimaryNavItem title="Add a Space" href="/spaces" class="tooltip-right">
<ImageIcon alt="Add a Space" src={Compass} size={8} />
</PrimaryNavItem>
{/each}
+3 -2
View File
@@ -9,9 +9,10 @@
type Props = {
url: string
hideFavorites?: boolean
}
const {url}: Props = $props()
const {url, hideFavorites}: Props = $props()
const rooms = deriveUserRooms(url)
const favorited = deriveGroupListPubkeys(url)
</script>
@@ -43,7 +44,7 @@
</div>
<RelayDescription {url} />
</div>
{#if $favorited.size > 0}
{#if !hideFavorites && $favorited.size > 0}
<div class="row-2 card2 card2-sm bg-alt">
Favorited By:
<ProfileCircles pubkeys={Array.from($favorited)} />
+1 -23
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import Login from "@assets/icons/login-3.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import Compass from "@assets/icons/compass.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
@@ -14,12 +13,6 @@
import SpaceInviteAccept from "@app/components/SpaceInviteAccept.svelte"
import {pushModal} from "@app/util/modal"
type Props = {
hideDiscover?: boolean
}
const {hideDiscover}: Props = $props()
const startJoin = () => pushModal(SpaceInviteAccept)
</script>
@@ -30,23 +23,8 @@
<ModalSubtitle
>Spaces are places where communities come together to work, play, and hang out.</ModalSubtitle>
</ModalHeader>
{#if !hideDiscover}
<Link href="/discover">
<CardButton class="btn-primary">
{#snippet icon()}
<div><Icon icon={Compass} size={7} /></div>
{/snippet}
{#snippet title()}
<div>Explore Spaces</div>
{/snippet}
{#snippet info()}
<div>Join create, or browse spaces</div>
{/snippet}
</CardButton>
</Link>
{/if}
<Button onclick={startJoin}>
<CardButton class={hideDiscover ? "btn-primary" : "btn-neutral"}>
<CardButton class="btn-primary">
{#snippet icon()}
<div><Icon icon={Login} size={7} /></div>
{/snippet}
+2 -2
View File
@@ -3,7 +3,7 @@
import {displayRelayUrl, ManagementMethod} from "@welshman/util"
import {manageRelay, forceLoadRelay} from "@welshman/app"
import StickerSmileSquare from "@assets/icons/sticker-smile-square.svg?dataurl"
import Planet from "@assets/icons/planet-3.svg?dataurl"
import Widget from "@assets/icons/widget-4.svg?dataurl"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
import UploadMinimalistic from "@assets/icons/upload-minimalistic.svg?dataurl"
import {preventDefault} from "@lib/html"
@@ -164,7 +164,7 @@
{#if imagePreview}
<ImageIcon src={imagePreview} alt="" />
{:else}
<Icon icon={Planet} />
<Icon icon={Widget} />
{/if}
<input bind:value={values.name} class="grow" type="text" />
</label>
+13 -1
View File
@@ -12,9 +12,11 @@
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalTitle from "@lib/components/ModalTitle.svelte"
import {AbortError, TimeoutError} from "$lib/util"
import {displayRoom} from "@app/core/state"
import {joinVoiceRoom} from "@app/voice"
import {popModal} from "@app/util/modal"
import {pushToast} from "@app/util/toast"
type Props = {
url: string
@@ -45,6 +47,16 @@
const goBack = () => history.back()
const handleJoinError = (e: unknown) => {
if (e instanceof AbortError) return
console.error("Failed to join voice room", e)
let message = "Failed to join voice room"
if (e instanceof TimeoutError)
message = "Connection timed out. Please check your network and try again."
else if (e instanceof Error) message = e.message
pushToast({theme: "error", message})
}
const joinVoice = async () => {
popModal()
await joinVoiceRoom(
@@ -52,7 +64,7 @@
h,
startWithoutMic,
startWithoutMic ? undefined : selectedDeviceId || undefined,
)
).catch(handleJoinError)
}
</script>
+5 -2
View File
@@ -546,8 +546,11 @@ export const chatsById = call(() => {
const unsubscribers = [
on(repository, "update", ({added, removed}: RepositoryUpdate) => {
addEvents(added)
removeEvents(removed)
// Do this async so that profiles are populated
setTimeout(() => {
addEvents(added)
removeEvents(removed)
}, 50)
}),
]
+1 -2
View File
@@ -8,8 +8,7 @@ const FALLBACK_APP_NAME = "Flotilla"
const staticTitles = new Map<string, string>([
["/", "Redirecting"],
["/home", "Home"],
["/discover", "Join a Space"],
["/spaces", "Your Spaces"],
["/spaces", "Spaces"],
["/spaces/create", "Create a Space"],
["/spaces/[relay]", "Space"],
["/spaces/[relay]/chat", "Space Chat"],
-156
View File
@@ -1,156 +0,0 @@
<script lang="ts">
import {onMount} from "svelte"
import {derived as _derived} from "svelte/store"
import {dec, sleep} from "@welshman/lib"
import type {RelayProfile} from "@welshman/util"
import {throttled} from "@welshman/store"
import {relays, createSearch} from "@welshman/app"
import {createScroller} from "@lib/html"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import Login from "@assets/icons/login-3.svg?dataurl"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Page from "@lib/components/Page.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import Divider from "@lib/components/Divider.svelte"
import Button from "@lib/components/Button.svelte"
import CardButton from "@lib/components/CardButton.svelte"
import Link from "@lib/components/Link.svelte"
import PageHeader from "@lib/components/PageHeader.svelte"
import ContentSearch from "@lib/components/ContentSearch.svelte"
import SpaceInviteAccept from "@app/components/SpaceInviteAccept.svelte"
import RelaySummary from "@app/components/RelaySummary.svelte"
import SpaceJoin from "@app/components/SpaceJoin.svelte"
import {groupListPubkeysByUrl, parseInviteLink} from "@app/core/state"
import {pushModal} from "@app/util/modal"
const startJoin = () => pushModal(SpaceInviteAccept)
const relaySearch = _derived(throttled(1000, relays), $relays => {
const options = $relays.filter(r => $groupListPubkeysByUrl.has(r.url))
return createSearch(options, {
getValue: (relay: RelayProfile) => relay.url,
sortFn: ({score, item}) => {
if (score && score > 0.1) return -score!
const wotScore = $groupListPubkeysByUrl.get(item.url)!.size
return score ? dec(score) * wotScore : -wotScore
},
fuseOptions: {
keys: ["url", "name", {name: "description", weight: 0.3}],
shouldSort: false,
},
})
})
const openSpace = (url: string, claim = "") => {
if (claim) {
pushModal(SpaceInviteAccept, {invite: term})
} else {
pushModal(SpaceJoin, {url})
}
}
let term = $state("")
let limit = $state(20)
let element: Element
const options = $derived($relaySearch.searchOptions(term).filter(r => r.url !== inviteData?.url))
const inviteData = $derived(parseInviteLink(term))
onMount(() => {
const scroller = createScroller({
element,
onScroll: () => {
limit += 20
},
})
return () => {
scroller.stop()
}
})
</script>
<Page class="cw-full">
<ContentSearch>
{#snippet input()}
<div class="flex flex-col gap-2">
<PageHeader>
{#snippet title()}
Join a Space
{/snippet}
{#snippet info()}
Find communities all across the nostr network
{/snippet}
</PageHeader>
<div class="grid gap-3 sm:grid-cols-2 card2 bg-alt">
<Button onclick={startJoin} class="w-full">
<CardButton class="btn-primary w-full">
{#snippet icon()}
<div><Icon icon={Login} size={7} /></div>
{/snippet}
{#snippet title()}
<div>Join with an invite</div>
{/snippet}
{#snippet info()}
<div>Paste a link and jump right in.</div>
{/snippet}
</CardButton>
</Button>
<Link href="/spaces/create" class="w-full">
<CardButton class="btn-neutral w-full">
{#snippet icon()}
<div><Icon icon={AddCircle} size={7} /></div>
{/snippet}
{#snippet title()}
<div>Create a new space</div>
{/snippet}
{#snippet info()}
<div>Launch a place for your people.</div>
{/snippet}
</CardButton>
</Link>
</div>
<Divider>Or</Divider>
<div class="min-w-0">
<label class="input input-bordered flex items-center gap-2">
<Icon icon={Magnifier} />
<input bind:value={term} class="grow" type="text" placeholder="Search for spaces..." />
</label>
</div>
</div>
{/snippet}
{#snippet content()}
<div class="col-2" bind:this={element}>
{#if inviteData}
{#key inviteData.url}
<Button
class="card2 bg-alt shadow-md transition-all hover:shadow-lg hover:dark:brightness-[1.1]"
onclick={() => openSpace(inviteData.url, inviteData.claim)}>
<RelaySummary url={inviteData.url} />
</Button>
{/key}
{/if}
{#each options.slice(0, limit) as relay (relay.url)}
<Button
class="card2 bg-alt shadow-md transition-all hover:shadow-lg hover:dark:brightness-[1.1]"
onclick={() => openSpace(relay.url)}>
<RelaySummary url={relay.url} />
</Button>
{/each}
<div class="flex justify-center py-20">
{#await sleep(5000)}
<Spinner loading>Looking for spaces...</Spinner>
{:then}
{#if options.length === 0}
<Spinner>No spaces found.</Spinner>
{/if}
{/await}
</div>
</div>
{/snippet}
</ContentSearch>
</Page>
+1 -1
View File
@@ -25,7 +25,7 @@
<h1 class="text-center text-5xl">Welcome to</h1>
<h1 class="mb-4 text-center text-5xl font-bold uppercase">{PLATFORM_NAME}</h1>
<div class="col-3">
<Link href="/discover">
<Link href="/spaces">
<CardButton class="btn-neutral">
{#snippet icon()}
<Icon icon={AddCircle} size={7} />
+197 -49
View File
@@ -1,20 +1,69 @@
<script lang="ts">
import {insertAt, removeAt} from "@welshman/lib"
import Planet from "@assets/icons/planet-3.svg?dataurl"
import {onMount, tick} from "svelte"
import {derived as _derived} from "svelte/store"
import {dec, insertAt, removeAt, sleep} from "@welshman/lib"
import type {RelayProfile} from "@welshman/util"
import {throttled} from "@welshman/store"
import {relays, createSearch} from "@welshman/app"
import {createScroller} from "@lib/html"
import {fly} from "@lib/transition"
import Widget from "@assets/icons/widget-4.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Page from "@lib/components/Page.svelte"
import PageBar from "@lib/components/PageBar.svelte"
import PageContent from "@lib/components/PageContent.svelte"
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
import Divider from "@lib/components/Divider.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import RelaySummary from "@app/components/RelaySummary.svelte"
import SpaceAdd from "@app/components/SpaceAdd.svelte"
import {userSpaceUrls, loadUserGroupList, PLATFORM_RELAYS} from "@app/core/state"
import SpaceInviteAccept from "@app/components/SpaceInviteAccept.svelte"
import SpaceJoin from "@app/components/SpaceJoin.svelte"
import {
userSpaceUrls,
loadUserGroupList,
PLATFORM_RELAYS,
groupListPubkeysByUrl,
parseInviteLink,
} from "@app/core/state"
import {setSpaceMembershipOrder} from "@app/core/commands"
import {pushModal} from "@app/util/modal"
import {goToSpace} from "@app/util/routes"
const addSpace = () => pushModal(SpaceAdd)
const relaySearch = _derived(throttled(1000, relays), $relays => {
const options = $relays.filter(r => $groupListPubkeysByUrl.has(r.url))
return createSearch(options, {
getValue: (relay: RelayProfile) => relay.url,
sortFn: ({score, item}) => {
if (score && score > 0.1) return -score!
const wotScore = $groupListPubkeysByUrl.get(item.url)?.size || 0
return score ? dec(score) * wotScore : -wotScore
},
fuseOptions: {
keys: ["url", "name", {name: "description", weight: 0.3}],
shouldSort: false,
},
})
})
const openSpace = (url: string, claim = "") => {
if ($userSpaceUrls.includes(url)) {
goToSpace(url)
} else if (claim) {
pushModal(SpaceInviteAccept, {invite: term})
} else {
pushModal(SpaceJoin, {url})
}
}
const reconcileUrls = (currentUrls: string[], nextUrls: string[]) => {
const mergedUrls = currentUrls.filter(url => nextUrls.includes(url))
@@ -31,16 +80,12 @@
a.length === b.length && a.every((url, index) => url === b[index])
const reorderSpaceUrls = (targetUrl: string) => {
if (!draggedUrl) {
return
}
if (!draggedUrl) return
const sourceIndex = orderedSpaceUrls.indexOf(draggedUrl)
const targetIndex = orderedSpaceUrls.indexOf(targetUrl)
if (sourceIndex === -1 || targetIndex === -1 || sourceIndex === targetIndex) {
return
}
if (sourceIndex === -1 || targetIndex === -1 || sourceIndex === targetIndex) return
orderedSpaceUrls = insertAt(
targetIndex,
@@ -89,57 +134,160 @@
}
})
let term = $state("")
let showSearch = $state(false)
let searchInput: HTMLInputElement | undefined = $state()
let limit = $state(20)
let element: Element
let orderedSpaceUrls = $state<string[]>([])
let draggedUrl = $state<string | undefined>()
let dragStartOrder = $state<string[] | undefined>()
const openSearch = () => {
showSearch = true
tick().then(() => searchInput?.focus())
}
const closeSearch = () => {
showSearch = false
term = ""
}
const inviteData = $derived(parseInviteLink(term))
const searchResults = $derived($relaySearch.searchOptions(term))
const userSpaceSet = $derived(new Set($userSpaceUrls))
const filteredUserUrls = $derived(
term
? orderedSpaceUrls.filter(url => searchResults.some(r => r.url === url))
: orderedSpaceUrls,
)
const otherSpaces = $derived(
searchResults.filter(r => !userSpaceSet.has(r.url) && r.url !== inviteData?.url),
)
onMount(() => {
const scroller = createScroller({
element,
onScroll: () => {
limit += 20
},
})
return () => {
scroller.stop()
}
})
</script>
<Page class="cw-full">
<PageBar class="cw-full">
<div class="flex items-center justify-between gap-4">
<div class="ellipsize flex items-center gap-4 whitespace-nowrap">
<Icon icon={Planet} />
<strong>Your Spaces</strong>
</div>
{#if $userSpaceUrls.length > 0 && PLATFORM_RELAYS.length === 0}
<Button class="btn btn-primary btn-sm" onclick={addSpace}>
<Icon icon={AddCircle} />
Add Space
{#if showSearch}
<label class="input input-bordered input-sm flex flex-1 items-center gap-2" in:fly>
<Icon icon={Magnifier} />
<input
bind:this={searchInput}
bind:value={term}
class="min-w-0 grow"
type="text"
placeholder="Search for spaces..." />
<Button onclick={closeSearch} class="flex items-center">
<Icon icon={CloseCircle} />
</Button>
{/if}
</div>
</label>
{:else}
<div class="flex items-center justify-between gap-4" in:fly>
<div class="ellipsize flex items-center gap-2 whitespace-nowrap">
<Icon icon={Widget} size={6} />
<strong>Spaces</strong>
</div>
<div class="flex items-center gap-2">
<button
class="btn btn-neutral btn-sm btn-square"
aria-label="Search"
onclick={openSearch}>
<Icon size={4} icon={Magnifier} />
</button>
{#if PLATFORM_RELAYS.length === 0}
<Button class="btn btn-primary btn-sm" onclick={addSpace}>
<Icon icon={AddCircle} />
Add Space
</Button>
{/if}
</div>
</div>
{/if}
</PageBar>
<PageContent class="cw-full flex flex-col gap-2 p-2 pt-4">
{#each PLATFORM_RELAYS as url (url)}
<MenuSpacesItem {url} />
{:else}
{#await loadUserGroupList()}
<div class="flex justify-center items-center py-20">
<span class="loading loading-spinner mr-3"></span>
Loading your spaces...
</div>
{:then}
{#each orderedSpaceUrls as url (url)}
<div
class:opacity-60={draggedUrl === url}
draggable="true"
role="listitem"
ondragstart={e => onDragStart(e, url)}
ondragover={e => onDragOver(e, url)}
ondrop={e => onDrop(e, url)}
ondragend={onDragEnd}>
<MenuSpacesItem {url} />
<div class="flex flex-col gap-2" bind:this={element}>
{#each PLATFORM_RELAYS as url (url)}
<Button
class="card2 bg-alt shadow-md transition-all hover:shadow-lg hover:dark:brightness-[1.1]"
onclick={() => openSpace(url)}>
<RelaySummary {url} />
</Button>
{:else}
{#await loadUserGroupList()}
<div class="flex items-center justify-center py-20">
<span class="loading loading-spinner mr-3"></span>
Loading your spaces...
</div>
{:else}
<div class="flex flex-col gap-8 items-center py-20">
<p>You haven't added any spaces yet!</p>
<Button class="btn btn-primary" onclick={addSpace}>
<Icon icon={AddCircle} />
Add a Space
{:then}
{#if inviteData}
<Divider>Search results</Divider>
{#key inviteData.url}
<Button
class="card2 bg-alt shadow-md transition-all hover:shadow-lg hover:dark:brightness-[1.1]"
onclick={() => openSpace(inviteData.url, inviteData.claim)}>
<RelaySummary url={inviteData.url} />
</Button>
{/key}
{/if}
{#if filteredUserUrls.length > 0}
<Divider>Your spaces</Divider>
{#each filteredUserUrls as url (url)}
<div
class:opacity-60={draggedUrl === url}
draggable="true"
role="listitem"
ondragstart={e => onDragStart(e, url)}
ondragover={e => onDragOver(e, url)}
ondrop={e => onDrop(e, url)}
ondragend={onDragEnd}>
<Button
class="card2 bg-alt shadow-md transition-all hover:shadow-lg hover:dark:brightness-[1.1] w-full"
onclick={() => openSpace(url)}>
<RelaySummary hideFavorites {url} />
</Button>
</div>
{/each}
{:else if !term}
<div class="flex flex-col items-center gap-4 py-12 text-center">
<p class="text-base-content/60">You haven't joined any spaces yet.</p>
<Button class="btn btn-primary" onclick={addSpace}>
<Icon icon={AddCircle} />
Browse Spaces
</Button>
</div>
{/if}
<Divider>{filteredUserUrls.length > 0 ? "More Spaces" : "Browse Spaces"}</Divider>
{#each otherSpaces.slice(0, limit) as relay (relay.url)}
<Button
class="card2 bg-alt shadow-md transition-all hover:shadow-lg hover:dark:brightness-[1.1]"
onclick={() => openSpace(relay.url)}>
<RelaySummary url={relay.url} />
</Button>
{/each}
<div class="flex justify-center py-20">
{#await sleep(5000)}
<Spinner loading>Looking for spaces...</Spinner>
{:then}
{#if otherSpaces.length === 0}
<Spinner>No other spaces found.</Spinner>
{/if}
{/await}
</div>
{/each}
{/await}
{/each}
{/await}
{/each}
</div>
</PageContent>
</Page>
-19
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import Server from "@assets/icons/server.svg?dataurl"
import ArrowRight from "@assets/icons/arrow-right.svg?dataurl"
import HandShake from "@assets/icons/hand-shake.svg?dataurl"
import Link from "@lib/components/Link.svelte"
import Icon from "@lib/components/Icon.svelte"
import Page from "@lib/components/Page.svelte"
@@ -62,24 +61,6 @@
<Icon icon={ArrowRight} />
</Link>
</div>
<div class="card2 bg-alt flex flex-col gap-4">
<div class="flex flex-col gap-4">
<div class="flex items-center gap-2">
<Icon icon={HandShake} />
<h3 class="text-lg font-bold">Holis Communities</h3>
</div>
<ul class="flex list-inside list-disc flex-col gap-1 text-sm opacity-70">
<li>Simple self-serve space creation</li>
<li>Built-in moderation tools</li>
<li>Room-level access controls</li>
<li>Membship lists and invite codes</li>
</ul>
</div>
<Link external class="btn btn-neutral" href="https://hol.is">
Get Started
<Icon icon={ArrowRight} />
</Link>
</div>
<div class="card2 bg-alt flex flex-col gap-4">
<div class="flex flex-col gap-4">
<div class="self-start">