Fixing bugs with push notifications

This commit is contained in:
Jon Staab
2026-01-28 13:35:50 -08:00
parent bf6abd301c
commit 000344a942
6 changed files with 81 additions and 78 deletions
+6 -6
View File
@@ -4,13 +4,13 @@ const config: CapacitorConfig = {
appId: "social.flotilla", appId: "social.flotilla",
appName: "Flotilla", appName: "Flotilla",
webDir: "build", webDir: "build",
server: {
androidScheme: "https",
},
android: { android: {
adjustMarginsForEdgeToEdge: false, adjustMarginsForEdgeToEdge: false,
}, },
plugins: { plugins: {
CapacitorHttp: {
enabled: true,
},
SystemBars: { SystemBars: {
insetsHandling: "disable", insetsHandling: "disable",
}, },
@@ -26,10 +26,10 @@ const config: CapacitorConfig = {
autoClear: true, autoClear: true,
}, },
}, },
// Use this for live reload https://capacitorjs.com/docs/guides/live-reload
server: { server: {
url: "http://192.168.1.148:1847", // Use this for live reload https://capacitorjs.com/docs/guides/live-reload
cleartext: true, // url: "http://192.168.1.17:1847",
// cleartext: true,
}, },
} }
+1 -1
View File
@@ -79,7 +79,7 @@
"husky": "^9.1.7", "husky": "^9.1.7",
"idb": "^8.0.3", "idb": "^8.0.3",
"nostr-signer-capacitor-plugin": "^0.0.4", "nostr-signer-capacitor-plugin": "^0.0.4",
"nostr-tools": "^2.19.4", "nostr-tools": "^2.19.4",
"prettier-plugin-tailwindcss": "^0.6.14", "prettier-plugin-tailwindcss": "^0.6.14",
"qr-scanner": "^1.4.2", "qr-scanner": "^1.4.2",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
+7 -7
View File
@@ -5601,7 +5601,7 @@ snapshots:
rimraf: 4.4.1 rimraf: 4.4.1
semver: 7.7.3 semver: 7.7.3
tar: 6.2.1 tar: 6.2.1
tslib: 2.6.2 tslib: 2.8.1
xml2js: 0.5.0 xml2js: 0.5.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5849,7 +5849,7 @@ snapshots:
'@ionic/utils-array@2.1.6': '@ionic/utils-array@2.1.6':
dependencies: dependencies:
debug: 4.3.4 debug: 4.3.4
tslib: 2.6.2 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5858,7 +5858,7 @@ snapshots:
'@types/fs-extra': 8.1.5 '@types/fs-extra': 8.1.5
debug: 4.3.4 debug: 4.3.4
fs-extra: 9.1.0 fs-extra: 9.1.0
tslib: 2.6.2 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5876,7 +5876,7 @@ snapshots:
debug: 4.3.4 debug: 4.3.4
signal-exit: 3.0.7 signal-exit: 3.0.7
tree-kill: 1.2.2 tree-kill: 1.2.2
tslib: 2.6.2 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5894,7 +5894,7 @@ snapshots:
'@ionic/utils-stream@3.1.6': '@ionic/utils-stream@3.1.6':
dependencies: dependencies:
debug: 4.3.4 debug: 4.3.4
tslib: 2.6.2 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5914,7 +5914,7 @@ snapshots:
'@ionic/utils-terminal': 2.3.4 '@ionic/utils-terminal': 2.3.4
cross-spawn: 7.0.6 cross-spawn: 7.0.6
debug: 4.3.4 debug: 4.3.4
tslib: 2.6.2 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -5939,7 +5939,7 @@ snapshots:
slice-ansi: 4.0.0 slice-ansi: 4.0.0
string-width: 4.2.3 string-width: 4.2.3
strip-ansi: 6.0.1 strip-ansi: 6.0.1
tslib: 2.6.2 tslib: 2.8.1
untildify: 4.0.0 untildify: 4.0.0
wrap-ansi: 7.0.0 wrap-ansi: 7.0.0
transitivePeerDependencies: transitivePeerDependencies:
+1 -1
View File
@@ -26,7 +26,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon-180x180.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon-180x180.png" />
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover" data-sveltekit-preload-code="eager">
<div style="display: contents">%sveltekit.body%</div> <div style="display: contents">%sveltekit.body%</div>
<script <script
defer defer
+2 -2
View File
@@ -13,9 +13,9 @@
</script> </script>
<Link replaceState href={path}> <Link replaceState href={path}>
<CardButton class="btn-neutral shadow-md"> <CardButton class="btn-neutral shadow-md bg-alt">
{#snippet icon()} {#snippet icon()}
<RelayIcon {url} size={12} /> <RelayIcon {url} size={12} class="rounded-full" />
{/snippet} {/snippet}
{#snippet title()} {#snippet title()}
<div class="flex gap-1"> <div class="flex gap-1">
+64 -61
View File
@@ -31,10 +31,10 @@ import {
identity, identity,
now, now,
groupBy, groupBy,
postJson,
nth, nth,
nthEq, nthEq,
maybe, maybe,
throttle,
} from "@welshman/lib" } from "@welshman/lib"
import type {TrustedEvent, Filter} from "@welshman/util" import type {TrustedEvent, Filter} from "@welshman/util"
import {deriveEventsByIdByUrl} from "@welshman/store" import {deriveEventsByIdByUrl} from "@welshman/store"
@@ -349,7 +349,6 @@ interface IPushAdapter {
request: (prompt?: boolean) => Promise<string> request: (prompt?: boolean) => Promise<string>
disable: () => Promise<void> disable: () => Promise<void>
enable: () => Promise<void> enable: () => Promise<void>
stop: () => Promise<void>
} }
if (Capacitor.isNativePlatform()) { if (Capacitor.isNativePlatform()) {
@@ -411,14 +410,32 @@ class CapacitorNotifications implements IPushAdapter {
} }
if (!subscription) { if (!subscription) {
const channel = Capacitor.getPlatform() === "ios" ? "apns" : "fcm" try {
const url = buildUrl(PUSH_SERVER, "subscription", channel) const channel = Capacitor.getPlatform() === "ios" ? "apns" : "fcm"
const json = await postJson(url, {token}, {signal}) const url = buildUrl(PUSH_SERVER, "subscription", channel)
const res = await fetch(url, {
signal,
method: "POST",
body: JSON.stringify({token}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
})
if (json?.callback && json?.key) { if (!res.ok) {
notificationState.update(assoc("subscription", json)) console.warn(`Failed to register with push server (status ${res.status})`)
} else { } else {
console.warn("Failed to register with push server") const json = await res.json()
if (json?.callback && json?.key) {
notificationState.update(assoc("subscription", json))
} else {
console.warn("Failed to register with push server (bad response)")
}
}
} catch (e) {
console.warn("Failed to register with push server:", e)
} }
} }
} }
@@ -439,7 +456,6 @@ class CapacitorNotifications implements IPushAdapter {
} }
_syncRelay = async (relay: string, key: string, filters: Filter[], ignore: Filter[] = []) => { _syncRelay = async (relay: string, key: string, filters: Filter[], ignore: Filter[] = []) => {
console.log(`=== syncing ${relay} ${key}`)
const {subscription} = notificationState.get() const {subscription} = notificationState.get()
if (!subscription) { if (!subscription) {
@@ -486,8 +502,6 @@ class CapacitorNotifications implements IPushAdapter {
} }
_unsyncRelay = async (relay: string, keys: string[]) => { _unsyncRelay = async (relay: string, keys: string[]) => {
console.log(`=== unsyncing ${relay} ${keys.join(", ")}`)
const stuff = await this._getPushStuff(relay) const stuff = await this._getPushStuff(relay)
if (!stuff) { if (!stuff) {
@@ -517,7 +531,7 @@ class CapacitorNotifications implements IPushAdapter {
signal.addEventListener( signal.addEventListener(
"abort", "abort",
merged([userSpaceUrls, notificationSettings, userSettingsValues]).subscribe( merged([userSpaceUrls, notificationSettings, userSettingsValues]).subscribe(
([$userSpaceUrls, {spaces, mentions}, {muted_rooms}]) => { throttle(3000, ([$userSpaceUrls, {spaces, mentions}, {muted_rooms}]) => {
const filters = [{kinds: MESSAGE_KINDS}, makeCommentFilter(CONTENT_KINDS)] const filters = [{kinds: MESSAGE_KINDS}, makeCommentFilter(CONTENT_KINDS)]
for (const url of $userSpaceUrls) { for (const url of $userSpaceUrls) {
@@ -545,7 +559,7 @@ class CapacitorNotifications implements IPushAdapter {
} }
} }
} }
}, }),
), ),
) )
} }
@@ -554,7 +568,7 @@ class CapacitorNotifications implements IPushAdapter {
signal.addEventListener( signal.addEventListener(
"abort", "abort",
merged([userMessagingRelayList, notificationSettings]).subscribe( merged([userMessagingRelayList, notificationSettings]).subscribe(
([$userMessagingRelayList, {messages}]) => { throttle(3000, ([$userMessagingRelayList, {messages}]) => {
for (const url of getRelaysFromList($userMessagingRelayList)) { for (const url of getRelaysFromList($userMessagingRelayList)) {
if (messages) { if (messages) {
this._syncRelay(url, "messages", [{kinds: DM_KINDS, "#p": [pubkey.get()!]}]) this._syncRelay(url, "messages", [{kinds: DM_KINDS, "#p": [pubkey.get()!]}])
@@ -562,24 +576,29 @@ class CapacitorNotifications implements IPushAdapter {
this._unsyncRelay(url, ["messages"]) this._unsyncRelay(url, ["messages"])
} }
} }
}, }),
), ),
) )
} }
async enable() { async enable() {
this._controller = new AbortController() if (!this._controller) {
this._controller = new AbortController()
try { try {
await this._syncServer(this._controller.signal) await this._syncServer(this._controller.signal)
await this._syncSpaceSubscription(this._controller.signal) await this._syncSpaceSubscription(this._controller.signal)
await this._syncMessageSubscription(this._controller.signal) await this._syncMessageSubscription(this._controller.signal)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
}
} }
} }
async disable() { async disable() {
this._controller?.abort()
this._controller = undefined
const {subscription} = notificationState.get() const {subscription} = notificationState.get()
if (subscription) { if (subscription) {
@@ -602,11 +621,6 @@ class CapacitorNotifications implements IPushAdapter {
notificationState.set({}) notificationState.set({})
} }
async stop() {
this._controller?.abort()
this._controller = undefined
}
} }
class WebNotifications implements IPushAdapter { class WebNotifications implements IPushAdapter {
@@ -645,30 +659,28 @@ class WebNotifications implements IPushAdapter {
} }
async enable() { async enable() {
this._unsubscriber = onNotification(event => { if (!this._unsubscriber) {
const {push, messages, mentions, spaces} = notificationSettings.get() this._unsubscriber = onNotification(event => {
const {push, messages, mentions, spaces} = notificationSettings.get()
if (push && document.hidden && Notification?.permission === "granted") { if (push && document.hidden && Notification?.permission === "granted") {
if (messages && matchFilters(dmFilters, event)) { if (messages && matchFilters(dmFilters, event)) {
this._notify(event, "New direct message", "Someone sent you a direct message.") this._notify(event, "New direct message", "Someone sent you a direct message.")
} else if ( } else if (
mentions && mentions &&
event.pubkey !== pubkey.get() && event.pubkey !== pubkey.get() &&
getPubkeyTagValues(event.tags).includes(pubkey.get()!) getPubkeyTagValues(event.tags).includes(pubkey.get()!)
) { ) {
this._notify(event, "Someone mentioned you", "Someone tagged you in a message.") this._notify(event, "Someone mentioned you", "Someone tagged you in a message.")
} else if (spaces) { } else if (spaces) {
this._notify(event, "New activity", "Someone posted a new message.") this._notify(event, "New activity", "Someone posted a new message.")
}
} }
} })
}) }
} }
async disable() { async disable() {
// pass
}
async stop() {
this._unsubscriber?.() this._unsubscriber?.()
this._unsubscriber = undefined this._unsubscriber = undefined
} }
@@ -696,27 +708,18 @@ export class Push {
static disable() { static disable() {
return Push._getAdapter().disable() return Push._getAdapter().disable()
} }
static enable() { static enable() {
return Push._getAdapter().enable() return Push._getAdapter().enable()
} }
static stop() {
return Push._getAdapter().stop()
}
static sync() { static sync() {
const adapter = Push._getAdapter() return notificationSettings.subscribe(({push}) => {
if (push) {
const unsubscriber = notificationSettings.subscribe($notificationSettings => { Push.enable()
if ($notificationSettings.push) {
adapter.enable()
} else { } else {
adapter.disable() Push.disable()
} }
}) })
return () => {
unsubscriber()
adapter.stop()
}
} }
} }