Compare commits

..

1 Commits

Author SHA1 Message Date
userAdityaa b42ba377e5 fix: deleted rooms persisting in navigation 2026-05-26 13:17:17 +05:30
5 changed files with 29 additions and 121 deletions
@@ -1,56 +0,0 @@
<script lang="ts">
import Bell from "@assets/icons/bell.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Modal from "@lib/components/Modal.svelte"
import ModalBody from "@lib/components/ModalBody.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalTitle from "@lib/components/ModalTitle.svelte"
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import {enableNotifications} from "@app/util/notifications"
import {pushToast} from "@app/util/toast"
const dismiss = () => history.back()
const enable = async () => {
loading = true
try {
if (await enableNotifications()) {
pushToast({message: "Notifications enabled!"})
dismiss()
} else {
pushToast({
theme: "error",
message: "Failed to request notification permissions.",
})
}
} finally {
loading = false
}
}
let loading = $state(false)
</script>
<Modal>
<ModalBody>
<ModalHeader>
<div class="flex justify-center">
<Icon icon={Bell} size={6} />
</div>
<ModalTitle>Enable notifications</ModalTitle>
<ModalSubtitle>
Get notified when you receive new direct messages, even when Flotilla is in the background.
</ModalSubtitle>
</ModalHeader>
</ModalBody>
<ModalFooter>
<Button class="btn btn-ghost" onclick={dismiss} disabled={loading}>Not now</Button>
<Button class="btn btn-primary" onclick={enable} disabled={loading}>
<Spinner {loading}>Enable</Spinner>
</Button>
</ModalFooter>
</Modal>
+1
View File
@@ -122,6 +122,7 @@
repository.removeEvent(thunk.event.id)
pushToast({theme: "error", message})
} else {
await removeRoomMembership(url, h)
goto(makeSpacePath(url))
}
},
+27 -8
View File
@@ -5,7 +5,7 @@ import {derived, readable, writable} from "svelte/store"
import * as nip19 from "nostr-tools/nip19"
import {
on,
gt,
gte,
max,
spec,
call,
@@ -417,9 +417,9 @@ export const device = withGetter(writable(randomId()))
export const notificationSettings = withGetter(
writable({
push: true,
sound: true,
badge: true,
push: false,
sound: false,
badge: false,
spaces: true,
mentions: true,
messages: true,
@@ -596,6 +596,9 @@ export const splitRoomId = (id: string) => id.split("'")
export const hasNip29 = (relay?: RelayProfile) =>
Boolean(relay?.supported_nips?.map?.(String)?.includes?.("29"))
const getRoomDeleteId = (event: TrustedEvent) =>
getTagValue("h", event.tags) || getTagValue("d", event.tags)
export const roomMetaEventsByIdByUrl = deriveEventsByIdByUrl({
tracker,
repository,
@@ -605,12 +608,25 @@ export const roomMetaEventsByIdByUrl = deriveEventsByIdByUrl({
export const roomsByUrl = derived(roomMetaEventsByIdByUrl, roomMetaEventsByIdByUrl => {
const result = new Map<string, Room[]>()
// Build a global set of deleted rooms from the repository directly so that
// deletes are visible even before the tracker has mapped them to a URL.
const deletedByH = new Map<string, number>()
for (const event of repository.query([{kinds: [ROOM_DELETE]}])) {
const h = getRoomDeleteId(event)
if (h) {
deletedByH.set(h, max([deletedByH.get(h), event.created_at]))
}
}
for (const [url, events] of roomMetaEventsByIdByUrl.entries()) {
const [metaEvents, deleteEvents] = partition(spec({kind: ROOM_META}), events.values())
const deletedByH = new Map<string, number>()
for (const event of deleteEvents) {
for (const h of getTagValues("h", event.tags)) {
const h = getRoomDeleteId(event)
if (h) {
deletedByH.set(h, max([deletedByH.get(h), event.created_at]))
}
}
@@ -620,7 +636,7 @@ export const roomsByUrl = derived(roomMetaEventsByIdByUrl, roomMetaEventsByIdByU
for (const event of metaEvents) {
const meta = tryCatch(() => readRoomMeta(event))
if (!meta || gt(deletedByH.get(meta.h), meta.event.created_at)) {
if (!meta || gte(deletedByH.get(meta.h), meta.event.created_at)) {
continue
}
@@ -651,7 +667,10 @@ export const loadRoom = call(() => {
await load({
relays: [url],
filters: [{kinds: [ROOM_META], "#d": [h]}],
filters: [
{kinds: [ROOM_META], "#d": [h]},
{kinds: [ROOM_DELETE], "#h": [h]},
],
})
}
+1 -45
View File
@@ -1,5 +1,4 @@
import {derived, get, writable} from "svelte/store"
import {Capacitor} from "@capacitor/core"
import {Badge} from "@capawesome/capacitor-badge"
import {synced, throttled, withGetter} from "@welshman/store"
import {pubkey, tracker, repository, relaysByUrl} from "@welshman/app"
@@ -11,7 +10,6 @@ import {makeSpacePath, makeRoomPath, makeSpaceChatPath, makeChatPath} from "@app
import {
CONTENT_KINDS,
notificationSettings,
pushState,
chatsById,
userGroupList,
getSpaceUrlsFromGroupList,
@@ -20,49 +18,7 @@ import {
} from "@app/core/state"
import {kv} from "@app/core/storage"
import {page} from "$app/stores"
import {Push} from "@app/util/push"
export {Push}
export const areNotificationsEnabled = () => {
const {push, messages} = notificationSettings.get()
if (!push || !messages) {
return false
}
if (Capacitor.isNativePlatform()) {
return Boolean(pushState.get().token)
}
return Notification?.permission === "granted"
}
export const enableNotifications = async () => {
const permission = await Push.request()
if (!permission.startsWith("granted")) {
return false
}
const current = notificationSettings.get()
notificationSettings.set({
...current,
push: true,
badge: true,
sound: Capacitor.isNativePlatform() ? current.sound : true,
messages: true,
})
return true
}
export const dmNotificationsPrompted = synced({
key: "dmNotificationsPrompted",
defaultValue: false,
storage: kv,
})
export {Push} from "@app/util/push"
// Checked state
-12
View File
@@ -1,25 +1,13 @@
<script lang="ts">
import {onMount} from "svelte"
import {get} from "svelte/store"
import {page} from "$app/stores"
import type {MakeNonOptional} from "@welshman/lib"
import {append, uniq} from "@welshman/lib"
import {pubkey} from "@welshman/app"
import Chat from "@app/components/Chat.svelte"
import EnableNotificationsPrompt from "@app/components/EnableNotificationsPrompt.svelte"
import {splitChatId} from "@app/core/state"
import {areNotificationsEnabled, dmNotificationsPrompted} from "@app/util/notifications"
import {pushModal} from "@app/util/modal"
const {chat} = $page.params as MakeNonOptional<typeof $page.params>
const pubkeys = uniq(append($pubkey!, splitChatId(chat)))
onMount(() => {
if (!areNotificationsEnabled() && !get(dmNotificationsPrompted)) {
dmNotificationsPrompted.set(true)
pushModal(EnableNotificationsPrompt)
}
})
</script>
<Chat {pubkeys} />