Improve coverage around desktop notifications. Closes #115.

This commit is contained in:
jkingsman
2026-03-26 16:39:38 -07:00
parent 54c24c50d3
commit bea3495b79
2 changed files with 137 additions and 12 deletions
+66 -9
View File
@@ -9,6 +9,17 @@ const NOTIFICATION_ICON_PATH = '/favicon-256x256.png';
type NotificationPermissionState = NotificationPermission | 'unsupported';
type ConversationNotificationMap = Record<string, boolean>;
interface NotificationEnableToastInfo {
level: 'success' | 'warning';
title: string;
description?: string;
}
interface NotificationEnvironment {
protocol: string;
isSecureContext: boolean;
}
function getConversationNotificationKey(type: 'channel' | 'contact', id: string): string {
return getStateKey(type, id);
}
@@ -92,6 +103,40 @@ function buildMessageNotificationHash(message: Message): string | null {
return null;
}
export function getNotificationEnableToastInfo(
environment?: Partial<NotificationEnvironment>
): NotificationEnableToastInfo {
if (typeof window === 'undefined') {
return { level: 'success', title: 'Notifications enabled' };
}
const protocol = environment?.protocol ?? window.location.protocol;
const isSecureContext = environment?.isSecureContext ?? window.isSecureContext;
if (protocol === 'http:') {
return {
level: 'warning',
title: 'Notifications enabled with warning',
description:
'Desktop notifications are on for this conversation, but you are using HTTP instead of HTTPS. Notifications will likely not work reliably.',
};
}
// Best-effort heuristic only. Browsers do not expose certificate trust details
// directly to page JS, so an HTTPS page that is not a secure context is the
// closest signal we have for an untrusted/self-signed setup.
if (protocol === 'https:' && !isSecureContext) {
return {
level: 'warning',
title: 'Notifications enabled with warning',
description:
'Desktop notifications are on for this conversation, but your HTTPS connection is untrusted, such as a self-signed certificate. Notification delivery may be inconsistent depending on your browser.',
};
}
return { level: 'success', title: 'Notifications enabled' };
}
export function useBrowserNotifications() {
const [permission, setPermission] = useState<NotificationPermissionState>(getInitialPermission);
const [enabledByConversation, setEnabledByConversation] =
@@ -110,8 +155,6 @@ export function useBrowserNotifications() {
const toggleConversationNotifications = useCallback(
async (type: 'channel' | 'contact', id: string, label: string) => {
const blockedDescription =
'Allow notifications in your browser settings, then try again. Some browsers may refuse notifications on non-HTTPS or self-signed HTTPS origins. Check your browser documentation for how to trust an insecure origin and the associated risks before doing so.';
const conversationKey = getConversationNotificationKey(type, id);
if (enabledByConversation[conversationKey]) {
setEnabledByConversation((prev) => {
@@ -120,20 +163,23 @@ export function useBrowserNotifications() {
writeStoredEnabledMap(next);
return next;
});
toast.success(`${label} notifications disabled`);
toast.success('Notifications disabled', {
description: `Desktop notifications are off for ${label}.`,
});
return;
}
if (permission === 'unsupported') {
toast.error('Browser notifications unavailable', {
toast.error('Notifications unavailable', {
description: 'This browser does not support desktop notifications.',
});
return;
}
if (permission === 'denied') {
toast.error('Browser notifications blocked', {
description: blockedDescription,
toast.error('Notifications blocked', {
description:
'Desktop notifications are blocked by your browser. Allow notifications in browser settings, then try again. Non-HTTPS or untrusted HTTPS origins may also prevent notifications from working reliably.',
});
return;
}
@@ -155,13 +201,24 @@ export function useBrowserNotifications() {
icon: NOTIFICATION_ICON_PATH,
tag: `meshcore-notification-preview-${conversationKey}`,
});
toast.success(`${label} notifications enabled`);
const toastInfo = getNotificationEnableToastInfo();
if (toastInfo.level === 'warning') {
toast.warning(toastInfo.title, {
description: toastInfo.description,
});
} else {
toast.success(toastInfo.title, {
description: `Desktop notifications are on for ${label}.`,
});
}
return;
}
toast.error('Browser notifications not enabled', {
toast.error('Notifications not enabled', {
description:
nextPermission === 'denied' ? blockedDescription : 'Permission request was dismissed.',
nextPermission === 'denied'
? 'Desktop notifications were denied by your browser. Allow notifications in browser settings, then try again.'
: 'The browser permission request was dismissed.',
});
},
[enabledByConversation, permission]