forked from coracle/flotilla
refactor notification syncing
This commit is contained in:
+10
-10
@@ -1,11 +1,11 @@
|
|||||||
import type { CapacitorConfig } from '@capacitor/cli';
|
import type {CapacitorConfig} from "@capacitor/cli"
|
||||||
|
|
||||||
const config: CapacitorConfig = {
|
const config: CapacitorConfig = {
|
||||||
appId: 'social.flotilla',
|
appId: "social.flotilla",
|
||||||
appName: 'Flotilla',
|
appName: "Flotilla",
|
||||||
webDir: 'build'
|
webDir: "build",
|
||||||
server: {
|
server: {
|
||||||
androidScheme: "https"
|
androidScheme: "https",
|
||||||
},
|
},
|
||||||
android: {
|
android: {
|
||||||
adjustMarginsForEdgeToEdge: false,
|
adjustMarginsForEdgeToEdge: false,
|
||||||
@@ -23,14 +23,14 @@ const config: CapacitorConfig = {
|
|||||||
},
|
},
|
||||||
Badge: {
|
Badge: {
|
||||||
persist: true,
|
persist: true,
|
||||||
autoClear: true
|
autoClear: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Use this for live reload https://capacitorjs.com/docs/guides/live-reload
|
// Use this for live reload https://capacitorjs.com/docs/guides/live-reload
|
||||||
server: {
|
server: {
|
||||||
url: "http://192.168.1.65:1847",
|
url: "http://192.168.1.148:1847",
|
||||||
cleartext: true
|
cleartext: true,
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
export default config;
|
export default config
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {getProfile, loadProfile} from "@welshman/app"
|
import {getProfile, loadProfile} from "@welshman/app"
|
||||||
import {isMobile} from '@lib/html'
|
import {isMobile} from "@lib/html"
|
||||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
+13
-9
@@ -24,6 +24,7 @@ import {
|
|||||||
addToMapKey,
|
addToMapKey,
|
||||||
identity,
|
identity,
|
||||||
always,
|
always,
|
||||||
|
randomId,
|
||||||
tryCatch,
|
tryCatch,
|
||||||
fromPairs,
|
fromPairs,
|
||||||
remove,
|
remove,
|
||||||
@@ -337,6 +338,8 @@ export const relaysMostlyRestricted = writable<Record<string, string>>({})
|
|||||||
|
|
||||||
// Push notifications
|
// Push notifications
|
||||||
|
|
||||||
|
export const device = withGetter(writable(randomId()))
|
||||||
|
|
||||||
export const notificationSettings = withGetter(
|
export const notificationSettings = withGetter(
|
||||||
writable({
|
writable({
|
||||||
push: false,
|
push: false,
|
||||||
@@ -348,16 +351,17 @@ export const notificationSettings = withGetter(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const notificationState = withGetter(
|
export type PushSubscription = {
|
||||||
writable<{
|
key: string
|
||||||
token?: string
|
callback: string
|
||||||
subscription?: {
|
}
|
||||||
key: string
|
|
||||||
callback: string
|
|
||||||
}
|
|
||||||
}>({}),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
export type PushState = {
|
||||||
|
token?: string
|
||||||
|
subscription?: PushSubscription
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notificationState = withGetter(writable<PushState>({}))
|
||||||
|
|
||||||
// Chats
|
// Chats
|
||||||
|
|
||||||
|
|||||||
+208
-224
@@ -1,10 +1,10 @@
|
|||||||
import type {Unsubscriber, Subscriber} from "svelte/store"
|
import type {Unsubscriber, Readable, Subscriber} from "svelte/store"
|
||||||
import {derived, get} from "svelte/store"
|
import {derived, get} from "svelte/store"
|
||||||
import {Capacitor} from "@capacitor/core"
|
import {Capacitor} from "@capacitor/core"
|
||||||
import {Badge} from "@capawesome/capacitor-badge"
|
import {Badge} from "@capawesome/capacitor-badge"
|
||||||
import {PushNotifications} from "@capacitor/push-notifications"
|
import {PushNotifications} from "@capacitor/push-notifications"
|
||||||
import type {ActionPerformed, RegistrationError, Token} from "@capacitor/push-notifications"
|
import type {ActionPerformed, RegistrationError, Token} from "@capacitor/push-notifications"
|
||||||
import {synced, throttled, deriveDeduplicated, deriveDeduplicatedByValue} from "@welshman/store"
|
import {synced, throttled} from "@welshman/store"
|
||||||
import {
|
import {
|
||||||
pubkey,
|
pubkey,
|
||||||
tracker,
|
tracker,
|
||||||
@@ -34,8 +34,9 @@ import {
|
|||||||
postJson,
|
postJson,
|
||||||
nth,
|
nth,
|
||||||
nthEq,
|
nthEq,
|
||||||
|
maybe,
|
||||||
} from "@welshman/lib"
|
} from "@welshman/lib"
|
||||||
import type {TrustedEvent, RelayProfile, Filter} from "@welshman/util"
|
import type {TrustedEvent, Filter} from "@welshman/util"
|
||||||
import {deriveEventsByIdByUrl} from "@welshman/store"
|
import {deriveEventsByIdByUrl} from "@welshman/store"
|
||||||
import {
|
import {
|
||||||
ZAP_GOAL,
|
ZAP_GOAL,
|
||||||
@@ -82,10 +83,17 @@ import {
|
|||||||
userSpaceUrls,
|
userSpaceUrls,
|
||||||
splitRoomId,
|
splitRoomId,
|
||||||
makeRoomId,
|
makeRoomId,
|
||||||
|
device,
|
||||||
} from "@app/core/state"
|
} from "@app/core/state"
|
||||||
import {kv} from "@app/core/storage"
|
import {kv} from "@app/core/storage"
|
||||||
import {goto} from "$app/navigation"
|
import {goto} from "$app/navigation"
|
||||||
|
|
||||||
|
// Temporarily copied from welshman
|
||||||
|
|
||||||
|
type Stores = Readable<any> | [Readable<any>, ...Array<Readable<any>>] | Array<Readable<any>>
|
||||||
|
|
||||||
|
const merged = <S extends Stores>(stores: S) => derived(stores, identity)
|
||||||
|
|
||||||
// Checked state
|
// Checked state
|
||||||
|
|
||||||
export const checked = synced<Record<string, number>>({
|
export const checked = synced<Record<string, number>>({
|
||||||
@@ -313,9 +321,9 @@ export const onNotification = call(() => {
|
|||||||
// Badges
|
// Badges
|
||||||
|
|
||||||
export const syncBadges = () =>
|
export const syncBadges = () =>
|
||||||
derived([notifications, userSettingsValues], identity).subscribe(
|
derived([notifications, notificationSettings], identity).subscribe(
|
||||||
async ([$notifications, {alerts_badge}]) => {
|
async ([$notifications, $notificationSettings]) => {
|
||||||
if (alerts_badge) {
|
if ($notificationSettings.badge) {
|
||||||
try {
|
try {
|
||||||
await Badge.set({count: $notifications.size})
|
await Badge.set({count: $notifications.size})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -339,16 +347,15 @@ export const clearBadges = async () => {
|
|||||||
|
|
||||||
interface IPushAdapter {
|
interface IPushAdapter {
|
||||||
request: (prompt?: boolean) => Promise<string>
|
request: (prompt?: boolean) => Promise<string>
|
||||||
enable: () => Promise<void>
|
|
||||||
disable: () => Promise<void>
|
disable: () => Promise<void>
|
||||||
start: () => Unsubscriber
|
enable: () => Promise<void>
|
||||||
|
stop: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Capacitor.isNativePlatform()) {
|
if (Capacitor.isNativePlatform()) {
|
||||||
PushNotifications.addListener(
|
PushNotifications.addListener(
|
||||||
"pushNotificationActionPerformed",
|
"pushNotificationActionPerformed",
|
||||||
async (action: ActionPerformed) => {
|
async (action: ActionPerformed) => {
|
||||||
console.log('=======', JSON.stringify(action))
|
|
||||||
const event = parseJson(action.notification.data.event)
|
const event = parseJson(action.notification.data.event)
|
||||||
const relays = [action.notification.data.relay]
|
const relays = [action.notification.data.relay]
|
||||||
|
|
||||||
@@ -358,6 +365,8 @@ if (Capacitor.isNativePlatform()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CapacitorNotifications implements IPushAdapter {
|
class CapacitorNotifications implements IPushAdapter {
|
||||||
|
_controller = maybe<AbortController>()
|
||||||
|
|
||||||
async request(prompt = true) {
|
async request(prompt = true) {
|
||||||
let status = await PushNotifications.checkPermissions()
|
let status = await PushNotifications.checkPermissions()
|
||||||
|
|
||||||
@@ -394,7 +403,7 @@ class CapacitorNotifications implements IPushAdapter {
|
|||||||
return token ? "granted" : "denied"
|
return token ? "granted" : "denied"
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncServer(signal: AbortSignal) {
|
async _syncServer(signal: AbortSignal) {
|
||||||
const {token, subscription} = notificationState.get()
|
const {token, subscription} = notificationState.get()
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -414,205 +423,160 @@ class CapacitorNotifications implements IPushAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncRelays(signal: AbortSignal) {
|
_getSubscriptionIdentifier = (relay: string, key: string) =>
|
||||||
|
String(hash(relay + key + device.get()))
|
||||||
|
|
||||||
|
_getPushStuff = async (url: string) => {
|
||||||
|
let relay = await loadRelay(url)
|
||||||
|
|
||||||
|
if (!relay?.self || !relay?.supported_nips?.map(String)?.includes("9a")) {
|
||||||
|
relay = await loadRelay(PUSH_BRIDGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relay?.self) {
|
||||||
|
return {url: relay.url, pubkey: relay.self}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncRelay = async (relay: string, key: string, filters: Filter[], ignore: Filter[] = []) => {
|
||||||
|
console.log(`=== syncing ${relay} ${key}`)
|
||||||
const {subscription} = notificationState.get()
|
const {subscription} = notificationState.get()
|
||||||
|
|
||||||
if (signal.aborted) {
|
if (!subscription) {
|
||||||
|
console.warn(`Failed to subscribe ${relay} to notifications: no subscription`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subscription) {
|
const stuff = await this._getPushStuff(relay)
|
||||||
throw new Error("Attempted to sync relays without a subscription")
|
|
||||||
|
if (!stuff) {
|
||||||
|
console.warn(`Failed to subscribe ${relay} to notifications: unsupported`)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const canPush = (relay?: RelayProfile) =>
|
const {url, pubkey} = stuff
|
||||||
Boolean(relay?.self && relay?.supported_nips?.map(String)?.includes("9a"))
|
const identifier = this._getSubscriptionIdentifier(relay, key)
|
||||||
|
|
||||||
const getPushStuff = async (url: string) => {
|
const thunk = publishThunk({
|
||||||
let relay = await loadRelay(url)
|
relays: [url],
|
||||||
|
event: makeEvent(30390, {
|
||||||
|
content: await signer
|
||||||
|
.get()
|
||||||
|
.nip44.encrypt(
|
||||||
|
pubkey,
|
||||||
|
JSON.stringify([
|
||||||
|
["relay", relay],
|
||||||
|
["callback", subscription.callback],
|
||||||
|
...ignore.map(filter => ["ignore", JSON.stringify(filter)]),
|
||||||
|
...filters.map(filter => ["filter", JSON.stringify(filter)]),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
tags: [
|
||||||
|
["d", identifier],
|
||||||
|
["p", pubkey],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
if (!canPush(relay)) {
|
const error = await waitForThunkError(thunk)
|
||||||
relay = await loadRelay(PUSH_BRIDGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (relay?.self) {
|
if (error) {
|
||||||
return {url: relay.url, pubkey: relay.self}
|
console.warn(`Failed to subscribe ${relay} to ${key} notifications:`, error)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncRelay = async (
|
|
||||||
key: string,
|
|
||||||
relay: string,
|
|
||||||
filters: Filter[],
|
|
||||||
ignore: Filter[] = [],
|
|
||||||
) => {
|
|
||||||
const stuff = await getPushStuff(relay)
|
|
||||||
|
|
||||||
if (!stuff) {
|
|
||||||
console.warn(`Failed to subscribe ${relay} to ${key} notifications: unsupported`)
|
|
||||||
} else {
|
|
||||||
const {url, pubkey} = stuff
|
|
||||||
const identifier = String(hash(subscription.callback + relay + key))
|
|
||||||
|
|
||||||
const thunk = publishThunk({
|
|
||||||
signal,
|
|
||||||
relays: [url],
|
|
||||||
event: makeEvent(30390, {
|
|
||||||
content: await signer
|
|
||||||
.get()
|
|
||||||
.nip44.encrypt(
|
|
||||||
pubkey,
|
|
||||||
JSON.stringify([
|
|
||||||
["relay", relay],
|
|
||||||
["callback", subscription.callback],
|
|
||||||
...ignore.map(filter => ["ignore", JSON.stringify(filter)]),
|
|
||||||
...filters.map(filter => ["filter", JSON.stringify(filter)]),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
tags: [
|
|
||||||
["d", identifier],
|
|
||||||
["p", pubkey],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
const error = await waitForThunkError(thunk)
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.warn(`Failed to subscribe ${relay} to ${key} notifications:`, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsyncRelay = async (key: string, relay: string) => {
|
|
||||||
const stuff = await getPushStuff(relay)
|
|
||||||
|
|
||||||
if (!stuff) {
|
|
||||||
console.warn(`Failed to unsubscribe ${relay} from ${key} notifications: unsupported`)
|
|
||||||
} else {
|
|
||||||
const identifier = String(hash(subscription.callback + relay + key))
|
|
||||||
const address = new Address(30390, pubkey.get()!, identifier).toString()
|
|
||||||
|
|
||||||
const thunk = publishThunk({
|
|
||||||
signal,
|
|
||||||
relays: [stuff.url],
|
|
||||||
event: makeEvent(DELETE, {tags: [["a", address]]}),
|
|
||||||
})
|
|
||||||
|
|
||||||
const error = await waitForThunkError(thunk)
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.warn(`Failed to unsubscribe ${relay} from ${key} notifications:`, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncedSpaceUrls = new Set<string>()
|
|
||||||
|
|
||||||
const syncSpaceRelay = (url: string) => {
|
|
||||||
const {spaces, mentions} = notificationSettings.get()
|
|
||||||
const filters = [{kinds: MESSAGE_KINDS}, makeCommentFilter(CONTENT_KINDS)]
|
|
||||||
const mutedRooms = getSettings().muted_rooms.map(splitRoomId).filter(nthEq(0, url)).map(nth(1))
|
|
||||||
|
|
||||||
if (spaces) {
|
|
||||||
syncRelay("spaces", url, filters, [{"#h": [mutedRooms]}])
|
|
||||||
} else {
|
|
||||||
unsyncRelay("spaces", url)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mentions) {
|
|
||||||
const mentionFilters = filters.map(assoc("#p", [pubkey.get()!]))
|
|
||||||
|
|
||||||
if (!spaces) {
|
|
||||||
syncRelay("mentions", url, mentionFilters)
|
|
||||||
} else if (mutedRooms.length > 0) {
|
|
||||||
syncRelay("mentions", url, mentionFilters.map(assoc('#h', [mutedRooms])))
|
|
||||||
} else {
|
|
||||||
unsyncRelay("mentions", url)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsyncRelay("mentions", url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncSpaceRelays = () => {
|
|
||||||
const $userSpaceUrls = get(userSpaceUrls)
|
|
||||||
const {spaces, mentions} = notificationSettings.get()
|
|
||||||
|
|
||||||
for (const url of $userSpaceUrls) {
|
|
||||||
syncSpaceRelay(url)
|
|
||||||
syncedSpaceUrls.add(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const url of syncedSpaceUrls) {
|
|
||||||
if (!$userSpaceUrls.includes(url)) {
|
|
||||||
unsyncRelay("spaces", url)
|
|
||||||
syncedSpaceUrls.delete(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncedMessagingUrls = new Set<string>()
|
|
||||||
|
|
||||||
const syncMessagingRelay = (url: string) => {
|
|
||||||
const {messages} = notificationSettings.get()
|
|
||||||
|
|
||||||
if (messages) {
|
|
||||||
syncRelay("messages", url, [{kinds: DM_KINDS, "#p": [pubkey.get()!]}])
|
|
||||||
} else {
|
|
||||||
unsyncRelay("messages", url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncMessagingRelays = () => {
|
|
||||||
const messagingRelayUrls = getRelaysFromList(get(userMessagingRelayList))
|
|
||||||
|
|
||||||
for (const url of messagingRelayUrls) {
|
|
||||||
syncMessagingRelay(url)
|
|
||||||
syncedMessagingUrls.add(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const url of syncedMessagingUrls) {
|
|
||||||
if (!messagingRelayUrls.includes(url)) {
|
|
||||||
unsyncRelay("messages", url)
|
|
||||||
syncedMessagingUrls.delete(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsubscribers = [
|
|
||||||
userSpaceUrls.subscribe(syncSpaceRelays),
|
|
||||||
userMessagingRelayList.subscribe(syncMessagingRelays),
|
|
||||||
userSettingsValues.subscribe(syncSpaceRelays),
|
|
||||||
userSettingsValues.subscribe(syncMessagingRelays),
|
|
||||||
]
|
|
||||||
|
|
||||||
signal.addEventListener("abort", () => unsubscribers.forEach(call))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
_unsyncRelay = async (relay: string, keys: string[]) => {
|
||||||
const {token} = notificationState.get()
|
console.log(`=== unsyncing ${relay} ${keys.join(", ")}`)
|
||||||
const controller = new AbortController()
|
|
||||||
const {signal} = controller
|
|
||||||
|
|
||||||
if (!token) {
|
const stuff = await this._getPushStuff(relay)
|
||||||
console.warn("Attempted to start push notifications without a callback")
|
|
||||||
} else {
|
if (!stuff) {
|
||||||
call(async () => {
|
console.warn(`Failed to unsubscribe ${relay} from notifications: unsupported`)
|
||||||
try {
|
return
|
||||||
await this.syncServer(signal)
|
|
||||||
await this.syncRelays(signal)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => controller.abort()
|
const {url} = stuff
|
||||||
|
const tags: string[][] = []
|
||||||
|
for (const key of keys) {
|
||||||
|
const identifier = this._getSubscriptionIdentifier(relay, key)
|
||||||
|
const address = new Address(30390, pubkey.get()!, identifier).toString()
|
||||||
|
|
||||||
|
tags.push(["a", address])
|
||||||
|
}
|
||||||
|
|
||||||
|
const thunk = publishThunk({relays: [url], event: makeEvent(DELETE, {tags})})
|
||||||
|
|
||||||
|
const error = await waitForThunkError(thunk)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.warn(`Failed to unsubscribe ${relay} from notifications:`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _syncSpaceSubscription(signal: AbortSignal) {
|
||||||
|
signal.addEventListener(
|
||||||
|
"abort",
|
||||||
|
merged([userSpaceUrls, notificationSettings, userSettingsValues]).subscribe(
|
||||||
|
([$userSpaceUrls, {spaces, mentions}, {muted_rooms}]) => {
|
||||||
|
const filters = [{kinds: MESSAGE_KINDS}, makeCommentFilter(CONTENT_KINDS)]
|
||||||
|
|
||||||
|
for (const url of $userSpaceUrls) {
|
||||||
|
if (!spaces && !mentions) {
|
||||||
|
this._unsyncRelay(url, ["spaces", "mentions"])
|
||||||
|
} else if (!spaces) {
|
||||||
|
this._unsyncRelay(url, ["spaces"])
|
||||||
|
} else if (!mentions) {
|
||||||
|
this._unsyncRelay(url, ["mentions"])
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutedRooms = muted_rooms.map(splitRoomId).filter(nthEq(0, url)).map(nth(1))
|
||||||
|
|
||||||
|
if (spaces) {
|
||||||
|
this._syncRelay(url, "spaces", filters, [{"#h": [mutedRooms]}])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentions) {
|
||||||
|
const mentionFilters = filters.map(assoc("#p", [pubkey.get()!]))
|
||||||
|
|
||||||
|
if (!spaces) {
|
||||||
|
this._syncRelay(url, "mentions", mentionFilters)
|
||||||
|
} else if (mutedRooms.length > 0) {
|
||||||
|
this._syncRelay(url, "mentions", mentionFilters.map(assoc("#h", [mutedRooms])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async _syncMessageSubscription(signal: AbortSignal) {
|
||||||
|
signal.addEventListener(
|
||||||
|
"abort",
|
||||||
|
merged([userMessagingRelayList, notificationSettings]).subscribe(
|
||||||
|
([$userMessagingRelayList, {messages}]) => {
|
||||||
|
for (const url of getRelaysFromList($userMessagingRelayList)) {
|
||||||
|
if (messages) {
|
||||||
|
this._syncRelay(url, "messages", [{kinds: DM_KINDS, "#p": [pubkey.get()!]}])
|
||||||
|
} else {
|
||||||
|
this._unsyncRelay(url, ["messages"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async enable() {
|
async enable() {
|
||||||
notificationSettings.update(assoc("push", true))
|
this._controller = new AbortController()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this._syncServer(this._controller.signal)
|
||||||
|
await this._syncSpaceSubscription(this._controller.signal)
|
||||||
|
await this._syncMessageSubscription(this._controller.signal)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async disable() {
|
async disable() {
|
||||||
@@ -628,12 +592,26 @@ class CapacitorNotifications implements IPushAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationSettings.update(assoc('push', false))
|
for (const url of get(userSpaceUrls)) {
|
||||||
|
this._unsyncRelay(url, ["spaces", "mentions"])
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const url of getRelaysFromList(get(userMessagingRelayList))) {
|
||||||
|
this._unsyncRelay(url, ["messages"])
|
||||||
|
}
|
||||||
|
|
||||||
notificationState.set({})
|
notificationState.set({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
this._controller?.abort()
|
||||||
|
this._controller = undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebNotifications implements IPushAdapter {
|
class WebNotifications implements IPushAdapter {
|
||||||
|
_unsubscriber = maybe<Unsubscriber>()
|
||||||
|
|
||||||
async request(prompt = true) {
|
async request(prompt = true) {
|
||||||
if (prompt && Notification?.permission === "default") {
|
if (prompt && Notification?.permission === "default") {
|
||||||
await Notification.requestPermission()
|
await Notification.requestPermission()
|
||||||
@@ -642,7 +620,7 @@ class WebNotifications implements IPushAdapter {
|
|||||||
return Notification?.permission || "denied"
|
return Notification?.permission || "denied"
|
||||||
}
|
}
|
||||||
|
|
||||||
notify(event: TrustedEvent, title: string, body: string) {
|
_notify(event: TrustedEvent, title: string, body: string) {
|
||||||
const notification = new Notification(title, {
|
const notification = new Notification(title, {
|
||||||
body,
|
body,
|
||||||
tag: event.id,
|
tag: event.id,
|
||||||
@@ -666,32 +644,33 @@ class WebNotifications implements IPushAdapter {
|
|||||||
document.addEventListener("visibilitychange", onVisibilityChange)
|
document.addEventListener("visibilitychange", onVisibilityChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
async enable() {
|
||||||
return onNotification(event => {
|
this._unsubscriber = onNotification(event => {
|
||||||
const {alerts_push, alerts_messages, alerts_mentions, alerts_spaces} = getSettings()
|
const {push, messages, mentions, spaces} = notificationSettings.get()
|
||||||
|
|
||||||
if (alerts_push && document.hidden && Notification?.permission === "granted") {
|
if (push && document.hidden && Notification?.permission === "granted") {
|
||||||
if (alerts_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 (
|
||||||
alerts_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 (alerts_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 enable() {
|
async disable() {
|
||||||
notificationSettings.update(assoc("push", true))
|
// pass
|
||||||
}
|
}
|
||||||
|
|
||||||
async disable() {
|
async stop() {
|
||||||
notificationSettings.update(assoc("push", false))
|
this._unsubscriber?.()
|
||||||
|
this._unsubscriber = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,29 +689,34 @@ export class Push {
|
|||||||
return Push._adapter
|
return Push._adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
static start() {
|
|
||||||
const adapter = Push._getAdapter()
|
|
||||||
const promise = adapter.request(false)
|
|
||||||
const controller = new AbortController()
|
|
||||||
|
|
||||||
promise.then(permissions => {
|
|
||||||
if (permissions === "granted" && !controller.signal.aborted) {
|
|
||||||
controller.signal.addEventListener("abort", adapter.start())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => controller.abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
static request() {
|
static request() {
|
||||||
return Push._getAdapter().request()
|
return Push._getAdapter().request()
|
||||||
}
|
}
|
||||||
|
|
||||||
static enable() {
|
|
||||||
return Push._getAdapter().enable()
|
|
||||||
}
|
|
||||||
|
|
||||||
static disable() {
|
static disable() {
|
||||||
return Push._getAdapter().disable()
|
return Push._getAdapter().disable()
|
||||||
}
|
}
|
||||||
|
static enable() {
|
||||||
|
return Push._getAdapter().enable()
|
||||||
|
}
|
||||||
|
static stop() {
|
||||||
|
return Push._getAdapter().stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
static sync() {
|
||||||
|
const adapter = Push._getAdapter()
|
||||||
|
|
||||||
|
const unsubscriber = notificationSettings.subscribe($notificationSettings => {
|
||||||
|
if ($notificationSettings.push) {
|
||||||
|
adapter.enable()
|
||||||
|
} else {
|
||||||
|
adapter.disable()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscriber()
|
||||||
|
adapter.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,12 @@
|
|||||||
import {setupAnalytics} from "@app/util/analytics"
|
import {setupAnalytics} from "@app/util/analytics"
|
||||||
import {authPolicy, blockPolicy, trustPolicy, mostlyRestrictedPolicy} from "@app/util/policies"
|
import {authPolicy, blockPolicy, trustPolicy, mostlyRestrictedPolicy} from "@app/util/policies"
|
||||||
import {kv, db} from "@app/core/storage"
|
import {kv, db} from "@app/core/storage"
|
||||||
import {userSettingsValues, notificationSettings, notificationState} from "@app/core/state"
|
import {
|
||||||
|
device,
|
||||||
|
userSettingsValues,
|
||||||
|
notificationSettings,
|
||||||
|
notificationState,
|
||||||
|
} from "@app/core/state"
|
||||||
import {syncApplicationData} from "@app/core/sync"
|
import {syncApplicationData} from "@app/core/sync"
|
||||||
import * as commands from "@app/core/commands"
|
import * as commands from "@app/core/commands"
|
||||||
import * as requests from "@app/core/requests"
|
import * as requests from "@app/core/requests"
|
||||||
@@ -87,6 +92,11 @@
|
|||||||
|
|
||||||
// Sync stuff to localstorage
|
// Sync stuff to localstorage
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
sync({
|
||||||
|
key: "device",
|
||||||
|
store: device,
|
||||||
|
storage: kv,
|
||||||
|
}),
|
||||||
sync({
|
sync({
|
||||||
key: "pubkey",
|
key: "pubkey",
|
||||||
store: pubkey,
|
store: pubkey,
|
||||||
@@ -139,7 +149,7 @@
|
|||||||
unsubscribers.push(syncKeyboard())
|
unsubscribers.push(syncKeyboard())
|
||||||
|
|
||||||
// Initialize background notifications
|
// Initialize background notifications
|
||||||
unsubscribers.push(notifications.Push.start())
|
unsubscribers.push(notifications.Push.sync())
|
||||||
|
|
||||||
// Listen for signer errors, report to user via toast
|
// Listen for signer errors, report to user via toast
|
||||||
unsubscribers.push(
|
unsubscribers.push(
|
||||||
|
|||||||
@@ -39,18 +39,11 @@
|
|||||||
|
|
||||||
settings.push = false
|
settings.push = false
|
||||||
|
|
||||||
pushToast({
|
return pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Failed to request notification permissions.",
|
message: "Failed to request notification permissions.",
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Push.enable()
|
|
||||||
await Push.start()
|
|
||||||
} else {
|
|
||||||
await Push.disable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!equals(muted_rooms, $userSettingsValues.muted_rooms)) {
|
if (!equals(muted_rooms, $userSettingsValues.muted_rooms)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user