cheogram-www
created pr with
cheogram-www/tauri-alpha.1
commented
Changes are available at https://tangled.org/mrry.band/cheogram-www on branch tauri-alpha.
added cheogram-www/tauri-alpha.2
1: 45e8ad7 = 1: 45e8ad7 Refactor service worker access
2: fab4022 ! 2: ce66e8f Move notifications code to one file
-: ------- > 3: 10e3574 Refactor notification code
3: d127ee0 ! 4: ba0891f npm run tauri init
4: 9e006bd = 5: 5b3abf9 Request microphone and camera usage on mac
5: 03d4934 = 6: 994a2e9 Tauri file system media store
6: b96216a = 7: 320fa8f Don't register or use serviceworker in tauri
7: faace0a = 8: e76db17 Open links in browser
8: 9c07308 ! 9: 761dd22 Tauri notifications
cmds
checkout latest patchset:
ssh pr.pico.sh pull cheogram-www:tauri-alpha | git am -3checkout specific patchset revision:
ssh pr.pico.sh pull cheogram-www:tauri-alpha [rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh cheogram-www:tauri-alphaadd comment to patch request:
ssh pr.pico.sh comment cheogram-www:tauri-alpha "lgtm!"
Patchset
cheogram-www/tauri-alpha.1
Refactor service worker access
Eric Roberts
Refactor notification related code
2026-10-29T14:50:23ZEric Roberts
npm run tauri init
2026-10-29T15:06:32ZEric Roberts
Request microphone and camera usage on mac
2026-10-16T14:01:30ZEric Roberts
Tauri file system media store
2026-10-20T19:29:32ZEric Roberts
Don't register or use serviceworker in tauri
2026-10-16T18:10:16ZEric Roberts
Open links in browser
2026-10-19T18:23:24ZEric Roberts
→ Tauri notifications
2026-10-20T18:39:35ZEric Roberts
2026-10-19T19:40:11Z
Tauri notifications
Eric Roberts
2026-10-19T19:40:11ZAdding a new code path in showNotification to call Tauri notification code. Released versions of notify-rust had a macOS bug that didn't allow the click handlers to work. It's been fixed just not released, so I'm pointing at their main branch so we get the fix. A couple places had to change because showNotification doesn't always return a Notification so we have to check for addEventListener. For the same reason I had to update notification ref to just care about getting a close function. Notifications are kind of weird in Tauri. If we just left everything like it was, then new Notification would result in a notification showing up. But the onAction handler we setup wouldn't get any of the notification data that we rely on in extra so we wouldn't be able to have clicking the notification take you to the chat (or really know anything about what the notification was for). We might be able to patch the plugin to fix this, but for now I think the easiest path forward is to send Tauri notifications explicitly. If we did the patch then we could presumably also do the addEventListener and not have the useEffect in App.tsx at all.
Semantic diff summary
0 added,
10 modified,
0 signature changed,
0 removed
across 4 analyzed files
(4 files skipped: unsupported file type)
+1
-0
package.json
#
+118
-0
src-tauri/Cargo.lock
#
| ... | ... | @@ -2038,6 +2039,20 @@ version = "0.4.34" | |
| 2038 | 2039 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2039 | 2040 | checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" | |
| 2040 | 2041 | ||
| 2042 | + | [[package]] | |
| 2043 | + | name = "mac-notification-sys" | |
| 2044 | + | version = "0.6.15" | |
| 2045 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2046 | + | checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" | |
| 2047 | + | dependencies = [ | |
| 2048 | + | "cc", | |
| 2049 | + | "log", | |
| 2050 | + | "objc2", | |
| 2051 | + | "objc2-foundation", | |
| 2052 | + | "time", | |
| 2053 | + | "uuid", | |
| 2054 | + | ] | |
| 2055 | + | ||
| 2041 | 2056 | [[package]] | |
| 2042 | 2057 | name = "markup5ever" | |
| 2043 | 2058 | version = "0.39.0" |
| ... | ... | @@ -2164,6 +2179,19 @@ version = "1.0.6" | |
| 2164 | 2179 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2165 | 2180 | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" | |
| 2166 | 2181 | ||
| 2182 | + | [[package]] | |
| 2183 | + | name = "notify-rust" | |
| 2184 | + | version = "4.18.2" | |
| 2185 | + | source = "git+https://github.com/hoodie/notify-rust?branch=main#ee6419cf764b896c7b0f9aed3515cf95982be4f4" | |
| 2186 | + | dependencies = [ | |
| 2187 | + | "futures-lite", | |
| 2188 | + | "log", | |
| 2189 | + | "mac-notification-sys", | |
| 2190 | + | "serde", | |
| 2191 | + | "tauri-winrt-notification", | |
| 2192 | + | "zbus", | |
| 2193 | + | ] | |
| 2194 | + | ||
| 2167 | 2195 | [[package]] | |
| 2168 | 2196 | name = "num-conv" | |
| 2169 | 2197 | version = "0.2.2" |
| ... | ... | @@ -2645,6 +2674,15 @@ version = "0.2.1" | |
| 2645 | 2674 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2646 | 2675 | checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" | |
| 2647 | 2676 | ||
| 2677 | + | [[package]] | |
| 2678 | + | name = "ppv-lite86" | |
| 2679 | + | version = "0.2.21" | |
| 2680 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2681 | + | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" | |
| 2682 | + | dependencies = [ | |
| 2683 | + | "zerocopy", | |
| 2684 | + | ] | |
| 2685 | + | ||
| 2648 | 2686 | [[package]] | |
| 2649 | 2687 | name = "precomputed-hash" | |
| 2650 | 2688 | version = "0.1.1" |
| ... | ... | @@ -2742,6 +2780,35 @@ version = "6.0.0" | |
| 2742 | 2780 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2743 | 2781 | checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" | |
| 2744 | 2782 | ||
| 2783 | + | [[package]] | |
| 2784 | + | name = "rand" | |
| 2785 | + | version = "0.9.5" | |
| 2786 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2787 | + | checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" | |
| 2788 | + | dependencies = [ | |
| 2789 | + | "rand_chacha", | |
| 2790 | + | "rand_core", | |
| 2791 | + | ] | |
| 2792 | + | ||
| 2793 | + | [[package]] | |
| 2794 | + | name = "rand_chacha" | |
| 2795 | + | version = "0.9.0" | |
| 2796 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2797 | + | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" | |
| 2798 | + | dependencies = [ | |
| 2799 | + | "ppv-lite86", | |
| 2800 | + | "rand_core", | |
| 2801 | + | ] | |
| 2802 | + | ||
| 2803 | + | [[package]] | |
| 2804 | + | name = "rand_core" | |
| 2805 | + | version = "0.9.5" | |
| 2806 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2807 | + | checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" | |
| 2808 | + | dependencies = [ | |
| 2809 | + | "getrandom 0.3.4", | |
| 2810 | + | ] | |
| 2811 | + | ||
| 2745 | 2812 | [[package]] | |
| 2746 | 2813 | name = "raw-window-handle" | |
| 2747 | 2814 | version = "0.6.2" |
| ... | ... | @@ -3594,6 +3661,26 @@ dependencies = [ | |
| 3594 | 3661 | "time", | |
| 3595 | 3662 | ] | |
| 3596 | 3663 | ||
| 3664 | + | [[package]] | |
| 3665 | + | name = "tauri-plugin-notification" | |
| 3666 | + | version = "2.7.0" | |
| 3667 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3668 | + | checksum = "b939d7b19c8af1381a72ef8474b3aba2b56c1ddac2c23f0c8e96abbcf86b198b" | |
| 3669 | + | dependencies = [ | |
| 3670 | + | "log", | |
| 3671 | + | "notify-rust", | |
| 3672 | + | "rand", | |
| 3673 | + | "serde", | |
| 3674 | + | "serde_json", | |
| 3675 | + | "serde_repr", | |
| 3676 | + | "tauri", | |
| 3677 | + | "tauri-plugin", | |
| 3678 | + | "thiserror 2.0.21", | |
| 3679 | + | "time", | |
| 3680 | + | "url", | |
| 3681 | + | "zbus", | |
| 3682 | + | ] | |
| 3683 | + | ||
| 3597 | 3684 | [[package]] | |
| 3598 | 3685 | name = "tauri-plugin-opener" | |
| 3599 | 3686 | version = "2.7.2" |
| ... | ... | @@ -3714,6 +3801,17 @@ dependencies = [ | |
| 3714 | 3801 | "toml 1.1.8+spec-1.1.0", | |
| 3715 | 3802 | ] | |
| 3716 | 3803 | ||
| 3804 | + | [[package]] | |
| 3805 | + | name = "tauri-winrt-notification" | |
| 3806 | + | version = "0.8.1" | |
| 3807 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3808 | + | checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36" | |
| 3809 | + | dependencies = [ | |
| 3810 | + | "thiserror 2.0.21", | |
| 3811 | + | "windows", | |
| 3812 | + | "windows-version", | |
| 3813 | + | ] | |
| 3814 | + | ||
| 3717 | 3815 | [[package]] | |
| 3718 | 3816 | name = "tempfile" | |
| 3719 | 3817 | version = "3.27.0" |
| ... | ... | @@ -4832,6 +4930,26 @@ dependencies = [ | |
| 4832 | 4930 | "serde", | |
| 4833 | 4931 | ] | |
| 4834 | 4932 | ||
| 4933 | + | [[package]] | |
| 4934 | + | name = "zerocopy" | |
| 4935 | + | version = "0.8.63" | |
| 4936 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4937 | + | checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" | |
| 4938 | + | dependencies = [ | |
| 4939 | + | "zerocopy-derive", | |
| 4940 | + | ] | |
| 4941 | + | ||
| 4942 | + | [[package]] | |
| 4943 | + | name = "zerocopy-derive" | |
| 4944 | + | version = "0.8.63" | |
| 4945 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4946 | + | checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" | |
| 4947 | + | dependencies = [ | |
| 4948 | + | "proc-macro2", | |
| 4949 | + | "quote", | |
| 4950 | + | "syn 2.0.119", | |
| 4951 | + | ] | |
| 4952 | + | ||
| 4835 | 4953 | [[package]] | |
| 4836 | 4954 | name = "zerofrom" | |
| 4837 | 4955 | version = "0.1.8" |
+4
-0
src-tauri/Cargo.toml
#
| ... | ... | @@ -30,7 +30,11 @@ tokio = { version = "1", features = ["time"] } | |
| 30 | 30 | sha1 = "0.10" | |
| 31 | 31 | sha2 = "0.10" | |
| 32 | 32 | uuid = { version = "1", features = ["v4"] } | |
| 33 | + | tauri-plugin-notification = "2.7" | |
| 33 | 34 | infer = "0.22.0" | |
| 34 | 35 | tempfile = "3.27.0" | |
| 35 | 36 | ||
| 36 | 37 | sanitize-filename = "0.6" | |
| 38 | + | ||
| 39 | + | [patch.crates-io] | |
| 40 | + | notify-rust = { git = "https://github.com/hoodie/notify-rust", branch = "main" } |
+1
-1
src-tauri/capabilities/default.json
#
| ... | ... | @@ -3,5 +3,5 @@ | |
| 3 | 3 | "identifier": "default", | |
| 4 | 4 | "description": "enables the default permissions", | |
| 5 | 5 | "windows": ["main"], | |
| 6 | - | "permissions": ["core:default", "opener:default"] | |
| 6 | + | "permissions": ["core:default", "opener:default", "notification:default", "core:window:allow-show", "core:window:allow-unminimize", "core:window:allow-set-focus"] | |
| 7 | 7 | } |
+1
-0
src-tauri/src/lib.rs
#
+22
-1
src/App.tsx
#
| ... | ... | @@ -10,6 +11,9 @@ import { CircularProgress } from "@mui/material"; | |
| 10 | 11 | import { newAccount } from "./components/Login"; | |
| 11 | 12 | import { Suspense, useEffect, useState } from "react"; | |
| 12 | 13 | import * as borogove from "borogove"; | |
| 14 | + | import { isTauri } from "@tauri-apps/api/core"; | |
| 15 | + | import { getCurrentWindow } from "@tauri-apps/api/window"; | |
| 16 | + | import { onAction } from "@tauri-apps/plugin-notification"; | |
| 13 | 17 | import { borogove_persistence_Sqlite as borogoveSqlite } from "borogove/sqlite-wasm"; | |
| 14 | 18 | import * as Sentry from "@sentry/browser"; | |
| 15 | 19 | import { stem } from "porter2"; |
| ... | ... | @@ -181,6 +185,23 @@ const AppInner = () => { | |
| 181 | 185 | return () => serviceWorker?.removeEventListener("message", handler); | |
| 182 | 186 | }, [accounts, navigate]); | |
| 183 | 187 | ||
| 188 | + | useEffect(() => { | |
| 189 | + | if (!isTauri()) return; | |
| 190 | + | ||
| 191 | + | const handler = onAction(async ({ actionId, notification }) => { | |
| 192 | + | if (actionId !== "tap" || !notification) return; | |
| 193 | + | const result = safeParse(NotificationDataSchema, notification.extra); | |
| 194 | + | if (result.success) | |
| 195 | + | handleNotificationClick(result.output, accounts, navigate); | |
| 196 | + | const appWindow = getCurrentWindow(); | |
| 197 | + | await appWindow.show(); | |
| 198 | + | await appWindow.unminimize(); | |
| 199 | + | await appWindow.setFocus(); | |
| 200 | + | }); | |
| 201 | + | ||
| 202 | + | return () => void handler.then((handler) => handler.unregister()); | |
| 203 | + | }, [accounts, navigate]); | |
| 204 | + | ||
| 184 | 205 | return ( | |
| 185 | 206 | <SettingsContext.Provider value={settings}> | |
| 186 | 207 | <Outlet |
+2
-3
src/components/Call.tsx
#
| ... | ... | @@ -153,7 +153,7 @@ const showCallNotification = async ( | |
| 153 | 153 | }; | |
| 154 | 154 | ||
| 155 | 155 | const notification = await showNotification(title, options); | |
| 156 | - | if (notification) { | |
| 156 | + | if (notification && "addEventListener" in notification) { | |
| 157 | 157 | let clicked = false; | |
| 158 | 158 | notification.addEventListener("click", () => { | |
| 159 | 159 | clicked = true; |
| ... | ... | @@ -277,7 +276,7 @@ export const Call = ({ | |
| 277 | 276 | }; | |
| 278 | 277 | }, [accounts]); | |
| 279 | 278 | ||
| 280 | - | const notificationRef = useRef<Notification | null>(null); | |
| 279 | + | const notificationRef = useRef<Pick<Notification, "close"> | null>(null); | |
| 281 | 280 | const stopNowRef = useRef(() => {}); | |
| 282 | 281 | const playingCancelledRef = useRef(false); | |
| 283 | 282 | const callStatusEvent = useEffectEvent(() => { |
+14
-3
src/notifications.ts
#
| ... | ... | @@ -1,6 +1,11 @@ | |
| 1 | 1 | import type { NavigateFunction } from "react-router"; | |
| 2 | 2 | import * as borogove from "borogove"; | |
| 3 | 3 | import * as v from "valibot"; | |
| 4 | + | import { isTauri } from "@tauri-apps/api/core"; | |
| 5 | + | import { | |
| 6 | + | removeActive, | |
| 7 | + | sendNotification, | |
| 8 | + | } from "@tauri-apps/plugin-notification"; | |
| 4 | 9 | import { href, readyServiceWorker } from "./util"; | |
| 5 | 10 | ||
| 6 | 11 | export const NotificationDataSchema = v.object({ |
| ... | ... | @@ -29,11 +34,17 @@ export const showNotification = async ( | |
| 29 | 34 | options: NotificationOptions & { data: NotificationData }, | |
| 30 | 35 | ) => { | |
| 31 | 36 | const serviceWorker = await readyServiceWorker; | |
| 32 | - | if (serviceWorker) { | |
| 37 | + | ||
| 38 | + | if (isTauri()) { | |
| 39 | + | const id = crypto.getRandomValues(new Int32Array(1))[0]; | |
| 40 | + | sendNotification({ id, title, body: options.body, extra: options.data }); | |
| 41 | + | return { close: () => removeActive([{ id }]) }; | |
| 42 | + | } else if (serviceWorker) { | |
| 33 | 43 | await serviceWorker.showNotification(title, options); | |
| 34 | 44 | return null; | |
| 45 | + | } else { | |
| 46 | + | return new Notification(title, options); | |
| 35 | 47 | } | |
| 36 | - | return new Notification(title, options); | |
| 37 | 48 | }; | |
| 38 | 49 | ||
| 39 | 50 | export const showMessageNotification = async ( |