signal-desktop/app/renderWindowsToast.tsx

89 lines
2.9 KiB
TypeScript
Raw Normal View History

2023-08-01 16:06:29 +00:00
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import type { WindowsNotificationData } from '../ts/services/notifications';
import { NotificationType } from '../ts/services/notifications';
import { missingCaseError } from '../ts/util/missingCaseError';
2023-11-02 19:42:31 +00:00
import {
2024-09-20 01:03:44 +00:00
cancelPresentingRoute,
2023-11-02 19:42:31 +00:00
showConversationRoute,
showWindowRoute,
startCallLobbyRoute,
} from '../ts/util/signalRoutes';
2023-08-01 16:06:29 +00:00
function pathToUri(path: string) {
return `file:///${encodeURI(path.replace(/\\/g, '/'))}`;
}
const Toast = (props: {
launch: string;
// Note: though React doesn't like it, Windows seems to require that this be camelcase
activationType: string;
children: React.ReactNode;
}) => React.createElement('toast', props);
const Visual = (props: { children: React.ReactNode }) =>
React.createElement('visual', props);
const Binding = (props: { template: string; children: React.ReactNode }) =>
React.createElement('binding', props);
const Text = (props: { id: string; children: React.ReactNode }) =>
React.createElement('text', props);
const Image = (props: { id: string; src: string; 'hint-crop': string }) =>
React.createElement('image', props);
export function renderWindowsToast({
avatarPath,
body,
conversationId,
heading,
messageId,
storyId,
type,
}: WindowsNotificationData): string {
// Note: with these templates, the first <text> is one line, bolded
// https://learn.microsoft.com/en-us/previous-versions/windows/apps/hh761494(v=win.10)?redirectedfrom=MSDN#toastimageandtext02
// https://learn.microsoft.com/en-us/previous-versions/windows/apps/hh761494(v=win.10)?redirectedfrom=MSDN#toasttext02
const image = avatarPath ? (
<Image id="1" src={pathToUri(avatarPath)} hint-crop="circle" />
) : null;
const template = avatarPath ? 'ToastImageAndText02' : 'ToastText02';
let launch: URL;
// Note:
// 1) this maps to the notify() function in services/notifications.ts
// 2) this also maps to the url-handling in main.ts
if (type === NotificationType.Message || type === NotificationType.Reaction) {
2023-11-02 19:42:31 +00:00
launch = showConversationRoute.toAppUrl({
conversationId,
messageId: messageId ?? null,
storyId: storyId ?? null,
});
2023-08-01 16:06:29 +00:00
} else if (type === NotificationType.IncomingGroupCall) {
2023-11-02 19:42:31 +00:00
launch = startCallLobbyRoute.toAppUrl({
conversationId,
});
2023-08-01 16:06:29 +00:00
} else if (type === NotificationType.IncomingCall) {
2023-11-02 19:42:31 +00:00
launch = showWindowRoute.toAppUrl({});
2023-08-01 16:06:29 +00:00
} else if (type === NotificationType.IsPresenting) {
2024-09-20 01:03:44 +00:00
launch = cancelPresentingRoute.toAppUrl({});
2023-08-01 16:06:29 +00:00
} else {
throw missingCaseError(type);
}
return renderToStaticMarkup(
<Toast launch={launch.href} activationType="protocol">
<Visual>
<Binding template={template}>
{image}
<Text id="1">{heading}</Text>
<Text id="2">{body}</Text>
</Binding>
</Visual>
</Toast>
);
}