From cdeb7afcaeb7e13cd89c01bbaee838fd18e22cdc Mon Sep 17 00:00:00 2001 From: Saksham Jain Date: Tue, 14 Apr 2026 19:27:23 +0530 Subject: [PATCH 1/3] feat: add native share support for space invites --- package.json | 1 + pnpm-lock.yaml | 12 +++++++++ src/app/components/SpaceInvite.svelte | 36 +++++++++++++++++++++------ src/assets/icons/native-share.svg | 4 +++ 4 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/assets/icons/native-share.svg diff --git a/package.json b/package.json index a9237ab9..9d804001 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "@capacitor/keyboard": "^8.0.0", "@capacitor/preferences": "^8.0.0", "@capacitor/push-notifications": "^8.0.0", + "@capacitor/share": "^8.0.1", "@capawesome/capacitor-android-dark-mode-support": "^8.0.0", "@capawesome/capacitor-badge": "^8.0.0", "@getalby/lightning-tools": "^6.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a6e25e0..1473dd50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: '@capacitor/push-notifications': specifier: ^8.0.0 version: 8.0.0(@capacitor/core@8.0.1) + '@capacitor/share': + specifier: ^8.0.1 + version: 8.0.1(@capacitor/core@8.0.1) '@capawesome/capacitor-android-dark-mode-support': specifier: ^8.0.0 version: 8.0.0(@capacitor/core@8.0.1) @@ -838,6 +841,11 @@ packages: peerDependencies: '@capacitor/core': '>=8.0.0' + '@capacitor/share@8.0.1': + resolution: {integrity: sha512-3cSBKBCJVon54rKDROP2rqGyeGks4pBh9TbaEk9S375Kbek/ZHe72N50zIa0Vn9Eac/SuhwgehO/mmA4CsUOiw==} + peerDependencies: + '@capacitor/core': '>=8.0.0' + '@capacitor/synapse@1.0.4': resolution: {integrity: sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==} @@ -6021,6 +6029,10 @@ snapshots: dependencies: '@capacitor/core': 8.0.1 + '@capacitor/share@8.0.1(@capacitor/core@8.0.1)': + dependencies: + '@capacitor/core': 8.0.1 + '@capacitor/synapse@1.0.4': {} '@capawesome/capacitor-android-dark-mode-support@8.0.0(@capacitor/core@8.0.1)': diff --git a/src/app/components/SpaceInvite.svelte b/src/app/components/SpaceInvite.svelte index 79a15a84..c4186372 100644 --- a/src/app/components/SpaceInvite.svelte +++ b/src/app/components/SpaceInvite.svelte @@ -3,7 +3,9 @@ import {sleep} from "@welshman/lib" import {request} from "@welshman/net" import {displayRelayUrl, getTagValue, RELAY_INVITE} from "@welshman/util" + import {Share} from "@capacitor/share" import LinkRound from "@assets/icons/link-round.svg?dataurl" + import NativeShare from "@assets/icons/native-share.svg?dataurl" import Copy from "@assets/icons/copy.svg?dataurl" import Spinner from "@lib/components/Spinner.svelte" import Field from "@lib/components/Field.svelte" @@ -28,6 +30,8 @@ const copyInvite = () => clip(invite) + const shareInvite = () => Share.share({url: invite}) + let claim = $state("") let loading = $state(true) @@ -74,17 +78,35 @@

Oops! It looks like you're not a member of this relay.

{:else}
- +
+ +
{#snippet input()} - + + +
{/snippet} + {#snippet info()}

This invite link can be used by clicking "Add Space" and pasting it there. diff --git a/src/assets/icons/native-share.svg b/src/assets/icons/native-share.svg new file mode 100644 index 00000000..56faff7c --- /dev/null +++ b/src/assets/icons/native-share.svg @@ -0,0 +1,4 @@ + + + + -- 2.52.0 From 132c7f031b4a820324bdcef88276456ffde8c79c Mon Sep 17 00:00:00 2001 From: bhavishy2801 Date: Tue, 14 Apr 2026 15:43:49 +0000 Subject: [PATCH 2/3] fix: remove deleted rooms from space navigation after refresh ## Summary This PR fixes a bug where deleting a room succeeded on the relay, but the room could still appear in space navigation (including after refresh). Fixes #194. ## Root Cause Room list derivation did not consistently treat delete events as authoritative in edge cases: - Delete timestamp aggregation could involve an undefined prior value. - Room meta events with the same second-level timestamp as a delete event were still considered active. ## Changes - Updated room delete timestamp aggregation logic to handle first delete event safely. - Updated room filtering condition so a room is excluded when its meta timestamp is less than or equal to the delete timestamp. - This ensures deleted rooms are not shown in navigation immediately or after reload. ## Validation - `pnpm run check` passed. - `pnpm run lint` passed. ## Testing - [x] In a NIP-29 space, create a temporary room. - [x] Delete the room from room details. - [x] Confirm it disappears from navigation immediately. - [x] Hard refresh and confirm it does not reappear. - [x] Reopen app/session and confirm it remains absent. - [x] Repeat by creating/deleting another room quickly (same-minute timestamp edge case). --- src/app/core/state.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/core/state.ts b/src/app/core/state.ts index 78e8576c..581b735f 100644 --- a/src/app/core/state.ts +++ b/src/app/core/state.ts @@ -615,7 +615,12 @@ export const roomsByUrl = derived(roomMetaEventsByIdByUrl, roomMetaEventsByIdByU for (const event of deleteEvents) { for (const h of getTagValues("h", event.tags)) { - deletedByH.set(h, max([deletedByH.get(h), event.created_at])) + const deletedAt = deletedByH.get(h) + + deletedByH.set( + h, + deletedAt === undefined ? event.created_at : max([deletedAt, event.created_at]), + ) } } @@ -623,8 +628,9 @@ export const roomsByUrl = derived(roomMetaEventsByIdByUrl, roomMetaEventsByIdByU for (const event of metaEvents) { const meta = tryCatch(() => readRoomMeta(event)) + const deletedAt = meta ? deletedByH.get(meta.h) : undefined - if (!meta || gt(deletedByH.get(meta.h), meta.event.created_at)) { + if (!meta || (deletedAt !== undefined && !gt(meta.event.created_at, deletedAt))) { continue } -- 2.52.0 From 9fa1375435e7d6411313cc20b2d2d51c791a2c43 Mon Sep 17 00:00:00 2001 From: Saksham Jain Date: Tue, 14 Apr 2026 23:05:49 +0530 Subject: [PATCH 3/3] fix: handle unsupported Share API gracefully --- src/app/components/SpaceInvite.svelte | 32 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/app/components/SpaceInvite.svelte b/src/app/components/SpaceInvite.svelte index c4186372..d332ab13 100644 --- a/src/app/components/SpaceInvite.svelte +++ b/src/app/components/SpaceInvite.svelte @@ -30,8 +30,17 @@ const copyInvite = () => clip(invite) - const shareInvite = () => Share.share({url: invite}) + const shareInvite = async () => { + if (!canShare) return + try { + await Share.share({url: invite}) + } catch (e) { + console.error(e) + } +} + + let canShare = $state(false) let claim = $state("") let loading = $state(true) @@ -45,6 +54,13 @@ }) onMount(async () => { + try { + const {value} = await Share.canShare() + canShare = value + } catch { + canShare = false + } + const [[event]] = await Promise.all([ request({ relays: [url], @@ -84,12 +100,14 @@ {#snippet input()}

- + {#if canShare} + + {/if}