tauri-app: 8b click-through notifications
OS notification body already arrived as a 'maarcadetweet:notification' DOM event (Phase 7b). The toast pill that surfaces the body is now clickable: left-click navigates to the URL the notification was about (at://<did>/<col>/<rkey> opens the thread, unknown URL falls back to the home view); right-click dismisses without navigating. tauri-plugin-notification v2.x does not expose a reliable OS-level notification-click callback (it only shows the notification), so the two-step pattern (OS click focuses the app + in-app toast click navigates) is the standard workaround. lastNotificationUrl is now $state so the toast title updates when a new notification arrives.
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@ Cargo.lock.bak
|
||||
crates/tauri-app/node_modules/
|
||||
crates/tauri-app/dist/
|
||||
crates/tauri-app/.svelte-kit/
|
||||
crates/tauri-app/.vite/
|
||||
.vite/
|
||||
crates/tauri-app/src-tauri/target/
|
||||
crates/tauri-app/src-tauri/gen/
|
||||
crates/tauri-app/src/assets/fonts/*.ttf
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2.1.9","results":[[":crates/tauri-app/src/lib/api/client.test.ts",{"duration":31.70154099999999,"failed":false}],[":crates/tauri-app/src/lib/utils/localstorage.test.ts",{"duration":6.449833000000012,"failed":false}]]}
|
||||
{"version":"2.1.9","results":[[":crates/tauri-app/src/lib/api/client.test.ts",{"duration":31.23195799999999,"failed":false}],[":crates/tauri-app/src/lib/utils/localstorage.test.ts",{"duration":5.796832999999992,"failed":false}]]}
|
||||
@@ -96,9 +96,42 @@
|
||||
function onNotification(e: Event) {
|
||||
const detail = (e as CustomEvent<{ title: string; body: string; url: string | null }>).detail;
|
||||
if (detail) {
|
||||
// Surface in the existing toast stack — works for both the
|
||||
// OS-notification click and the in-app events.
|
||||
pushToast("info", `notif: ${detail.title} — ${detail.body}`);
|
||||
// Surface in the existing toast stack. The toast pill is
|
||||
// clickable — clicking it navigates to the URL the notification
|
||||
// was about (e.g. an at:// post URI). For OS-level
|
||||
// notifications, the user has to first click the OS notification
|
||||
// (which focuses the app) and then click the toast in the app
|
||||
// to actually navigate; this is the limitation of the
|
||||
// tauri-plugin-notification v2.x click callbacks.
|
||||
pushToast(
|
||||
"info",
|
||||
`notif: ${detail.title} — ${detail.body}` + (detail.url ? " (click to open)" : ""),
|
||||
);
|
||||
// Save the URL on a per-toast basis via a side-channel so the
|
||||
// toast pill can navigate when clicked. We attach it to the
|
||||
// notification event for simplicity (re-look-up via last).
|
||||
lastNotificationUrl = detail.url ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
let lastNotificationUrl: string | null = $state(null);
|
||||
|
||||
function openLastNotification() {
|
||||
const url = lastNotificationUrl;
|
||||
if (!url) return;
|
||||
if (url.startsWith("at://")) {
|
||||
// at://<did>/app.twi.post/<rkey> or at://<did>/app.bsky.feed.post/<rkey>
|
||||
const parts = url.replace(/^at:\/\//, "").split("/");
|
||||
const rkey = parts[parts.length - 1];
|
||||
const did = parts[0];
|
||||
if (rkey && did) {
|
||||
// Use the existing thread-context machinery to open the post.
|
||||
void openThread(`${did}/app.twi.post/${rkey}`);
|
||||
lastNotificationUrl = null;
|
||||
}
|
||||
} else {
|
||||
// unknown scheme — just open home
|
||||
view = "home";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,8 +487,22 @@
|
||||
class="toast-pill"
|
||||
class:toast-pill--err={t.kind === "error"}
|
||||
type="button"
|
||||
onclick={() => (toasts = toasts.filter((x) => x.id !== t.id))}
|
||||
title="dismiss"
|
||||
onclick={() => {
|
||||
// If this toast was a notification with a URL, navigate
|
||||
// to it before dismissing. Otherwise just dismiss.
|
||||
if (lastNotificationUrl) {
|
||||
openLastNotification();
|
||||
}
|
||||
toasts = toasts.filter((x) => x.id !== t.id);
|
||||
}}
|
||||
oncontextmenu={(e) => {
|
||||
// Right-click dismisses without navigating — useful when
|
||||
// the user clicks a notification toast by mistake.
|
||||
e.preventDefault();
|
||||
lastNotificationUrl = null;
|
||||
toasts = toasts.filter((x) => x.id !== t.id);
|
||||
}}
|
||||
title={lastNotificationUrl ? "click to open · right-click to dismiss" : "dismiss"}
|
||||
>
|
||||
{t.kind === "error" ? "err" : "info"}: {t.text}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user