fix: add Tauri opener plugin for external URL handling and improve notification focus behavior

This commit is contained in:
2026-01-25 13:58:44 +11:00
parent 72ba2e200a
commit 156dfcf397
3 changed files with 69 additions and 8 deletions

View File

@@ -90,13 +90,15 @@ function InviteNotifications() {
const notify = useCallback(
(count: number) => {
const body = `You have ${count} new invitation request.`;
const invitesPath = getInboxInvitesPath();
if (isTauri()) {
sendNotification({
title: 'Invitation',
body,
path: invitesPath,
onClick: () => {
if (!window.closed) navigate(getInboxInvitesPath());
if (!window.closed) navigate(invitesPath);
},
});
} else {
@@ -108,7 +110,8 @@ function InviteNotifications() {
});
noti.onclick = () => {
if (!window.closed) navigate(getInboxInvitesPath());
window.focus();
if (!window.closed) navigate(invitesPath);
noti.close();
};
}
@@ -207,6 +210,7 @@ function MessageNotifications() {
});
noti.onclick = () => {
window.focus();
if (!window.closed) navigate(roomPath);
noti.close();
notifRef.current = undefined;

View File

@@ -1,5 +1,5 @@
/**
* Open an external URL using Tauri shell on desktop/mobile, or window.open in browser
* Open an external URL using Tauri opener plugin on desktop/mobile, or window.open in browser
* @param url The URL to open
*/
export const openExternalUrl = async (url: string): Promise<void> => {
@@ -9,11 +9,11 @@ export const openExternalUrl = async (url: string): Promise<void> => {
// Use new Function to avoid static analysis by Vite/Rollup
await new Function(
'url',
"return import('@tauri-apps/plugin-shell').then(m => m.open(url));"
"return import('@tauri-apps/plugin-opener').then(m => m.openUrl(url));"
)(url);
return;
} catch (err) {
console.warn('Tauri shell.open failed, falling back to window.open:', err);
console.warn('Tauri opener.openUrl failed, falling back to window.open:', err);
}
}
window.open(url, '_blank');
@@ -26,7 +26,23 @@ export const openExternalUrl = async (url: string): Promise<void> => {
let notificationTapCallback: ((path: string) => void) | null = null;
/**
* Set up listener for notification tap actions on mobile
* Bring the Tauri window to the front and focus it
*/
export const focusWindow = async (): Promise<void> => {
if (!isTauri()) return;
try {
const { getCurrentWindow } = await import('@tauri-apps/api/window');
const window = getCurrentWindow();
await window.unminimize();
await window.setFocus();
} catch (err) {
console.warn('Failed to focus window:', err);
}
};
/**
* Set up listener for notification tap/click actions
* Call this once at app startup with a navigation callback
*/
export const setupNotificationTapListener = async (onTap: (path: string) => void): Promise<void> => {
@@ -37,7 +53,10 @@ export const setupNotificationTapListener = async (onTap: (path: string) => void
try {
const { onAction } = await import('@tauri-apps/plugin-notification');
await onAction((notification) => {
await onAction(async (notification) => {
// Bring window to front first
await focusWindow();
// Get the path from extra data and navigate
const path = notification.notification?.extra?.path;
if (path && typeof path === 'string' && notificationTapCallback) {
@@ -115,7 +134,11 @@ const sendBrowserNotification = (options: {
});
if (onClick) {
notification.onclick = () => {
notification.onclick = async () => {
// Bring Tauri window to front if in Tauri
await focusWindow();
// Also use standard window.focus for browser
window.focus();
if (!window.closed) onClick();
notification.close();
};
@@ -124,6 +147,34 @@ const sendBrowserNotification = (options: {
return notification;
};
/** Flag to track if notification channel has been created on Android */
let notificationChannelCreated = false;
/**
* Create notification channel for Android
* Required for Android 8.0+ to show notifications
*/
const ensureNotificationChannel = async (): Promise<void> => {
if (notificationChannelCreated || !isAndroid()) return;
try {
const { createChannel, Importance } = await import('@tauri-apps/plugin-notification');
await createChannel({
id: 'messages',
name: 'Messages',
description: 'Message notifications from Matrix',
importance: Importance.High,
vibration: true,
sound: 'default',
});
notificationChannelCreated = true;
} catch (err) {
console.warn('Failed to create notification channel:', err);
}
};
/**
* Send a notification using Tauri's notification plugin
* Falls back to browser Notification API if not in Tauri
@@ -152,9 +203,14 @@ export const sendNotification = async (options: {
}
if (permissionGranted) {
// Ensure notification channel exists on Android
await ensureNotificationChannel();
await tauriSendNotification({
title,
body,
// Use the channel on Android
channelId: isAndroid() ? 'messages' : undefined,
// Store path in extra data for notification tap handling
extra: path ? { path } : undefined,
});