Add fcm push notifications

This commit is contained in:
Jon Staab
2025-06-24 14:15:36 -07:00
parent 6cca823ed4
commit 3655790e5f
19 changed files with 236 additions and 608 deletions
+40 -9
View File
@@ -1,18 +1,28 @@
<script lang="ts">
import {onMount} from "svelte"
import {preventDefault} from "@lib/html"
import {randomInt, displayList, TIMEZONE, identity} from "@welshman/lib"
import {displayRelayUrl, getTagValue, THREAD, MESSAGE, EVENT_TIME, COMMENT} from "@welshman/util"
import {ucFirst} from "@lib/util"
import {decrypt} from "@welshman/signer"
import {randomInt, parseJson, fromPairs, displayList, TIMEZONE, identity} from "@welshman/lib"
import {
displayRelayUrl,
getTagValue,
getAddress,
THREAD,
MESSAGE,
EVENT_TIME,
COMMENT,
} from "@welshman/util"
import type {Filter} from "@welshman/util"
import {makeIntersectionFeed, makeRelayFeed, feedFromFilters} from "@welshman/feeds"
import {pubkey} from "@welshman/app"
import {pubkey, signer, getThunkError} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import FieldInline from "@lib/components/FieldInline.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import {alerts, getMembershipUrls, getMembershipRoomsByUrl, userMembership} from "@app/state"
import {alerts, getMembershipUrls, userMembership, NOTIFIER_PUBKEY} from "@app/state"
import {loadAlertStatuses, requestRelayClaims} from "@app/requests"
import {publishAlert} from "@app/commands"
import type {AlertParams} from "@app/commands"
@@ -114,20 +124,41 @@
try {
// @ts-ignore
params[platform] = await getPushInfo()
params.description = `${ucFirst(platform)} push notification ${description}.`
} catch (e: any) {
return pushToast({
theme: "error",
message: String(e),
})
}
params.description = `Push notification alert ${description}.`
}
const thunk = await publishAlert(params)
// If we don't do this we'll get an event rejection
await Pool.get().get(NOTIFIER_RELAY).auth.attemptAuth()
await thunk.result
await loadAlertStatuses($pubkey!)
const thunk = await publishAlert(params)
const error = await getThunkError(thunk)
if (error) {
return pushToast({
theme: "error",
message: `Failed to send your alert to the notification server (${error}).`,
})
}
// Fetch our new status to make sure it's active
const address = getAddress(thunk.event)
const statusEvents = await loadAlertStatuses($pubkey!)
const statusEvent = statusEvents.find(event => getTagValue("d", event.tags) === address)
const statusTags = statusEvent
? parseJson(await decrypt(signer.get(), NOTIFIER_PUBKEY, statusEvent.content))
: []
const {status = "error", message = "Your alert was not activated"}: Record<string, string> =
fromPairs(statusTags)
if (status === "error") {
return pushToast({theme: "error", message})
}
pushToast({message: "Your alert has been successfully created!"})
back()
+8 -9
View File
@@ -1,12 +1,12 @@
<script lang="ts">
import {parseJson, nthEq} from "@welshman/lib"
import {parseJson} from "@welshman/lib"
import {displayFeeds} from "@welshman/feeds"
import {getAddress, getTagValue, getTagValues} from "@welshman/util"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import AlertDelete from "@app/components/AlertDelete.svelte"
import type {Alert} from "@app/state"
import {alertStatuses} from "@app/state"
import {deriveAlertStatus} from "@app/state"
import {pushModal} from "@app/modal"
type Props = {
@@ -15,8 +15,7 @@
const {alert}: Props = $props()
const address = $derived(getAddress(alert.event))
const status = $derived($alertStatuses.find(s => s.event.tags.some(nthEq(1, address))))
const status = deriveAlertStatus(getAddress(alert.event))
const cron = $derived(getTagValue("cron", alert.tags))
const channel = $derived(getTagValue("channel", alert.tags))
const feeds = $derived(getTagValues("feed", alert.tags))
@@ -39,24 +38,24 @@
</Button>
<div class="flex-inline gap-1">{description}</div>
</div>
{#if status}
{@const statusText = getTagValue("status", status.tags) || "error"}
{#if $status}
{@const statusText = getTagValue("status", $status.tags) || "error"}
{#if statusText === "ok"}
<span
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-base-content px-3 py-1 text-sm"
data-tip={getTagValue("message", status.tags)}>
data-tip={getTagValue("message", $status.tags)}>
Active
</span>
{:else if statusText === "pending"}
<span
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-base-content border-yellow-500 px-3 py-1 text-sm text-yellow-500"
data-tip={getTagValue("message", status.tags)}>
data-tip={getTagValue("message", $status.tags)}>
Pending
</span>
{:else}
<span
class="tooltip tooltip-left cursor-pointer rounded-full border border-solid border-error px-3 py-1 text-sm text-error"
data-tip={getTagValue("message", status.tags)}>
data-tip={getTagValue("message", $status.tags)}>
{statusText.replace("-", " ").replace(/^(.)/, x => x.toUpperCase())}
</span>
{/if}
+2 -2
View File
@@ -24,8 +24,8 @@
memberships,
deriveUserRooms,
deriveOtherRooms,
deviceAlerts,
hasNip29,
alerts,
} from "@app/state"
import {loadAlerts} from "@app/requests"
import {notifications} from "@app/notifications"
@@ -40,7 +40,7 @@
const calendarPath = makeSpacePath(url, "calendar")
const userRooms = deriveUserRooms(url)
const otherRooms = deriveOtherRooms(url)
const alert = $derived($alerts.find(a => getTagValue("feed", a.tags)?.includes(url)))
const alert = $derived($deviceAlerts.find(a => getTagValue("feed", a.tags)?.includes(url)))
const openMenu = () => {
showMenu = true