Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/host/browser/toasts.ts
5241 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { addDisposableListener } from '../../../../base/browser/dom.js';
7
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
8
import { Emitter, Event } from '../../../../base/common/event.js';
9
import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
10
import { IToastOptions, IToastResult } from './host.js';
11
12
export interface IShowToastController {
13
onDidCreateToast: (toast: IDisposable) => void;
14
onDidDisposeToast: (toast: IDisposable) => void;
15
}
16
17
export async function showBrowserToast(controller: IShowToastController, options: IToastOptions, token: CancellationToken): Promise<IToastResult> {
18
const toast = await triggerBrowserToast(options.title, {
19
detail: options.body,
20
sticky: !options.silent
21
});
22
23
if (!toast) {
24
return { supported: false, clicked: false };
25
}
26
27
const disposables = new DisposableStore();
28
controller.onDidCreateToast(toast);
29
30
const cts = new CancellationTokenSource(token);
31
32
disposables.add(toDisposable(() => {
33
controller.onDidDisposeToast(toast);
34
toast.dispose();
35
cts.dispose(true);
36
}));
37
38
return new Promise<IToastResult>(r => {
39
const resolve = (result: IToastResult) => {
40
r(result); // first return the result before...
41
disposables.dispose(); // ...disposing which would invalidate the result object
42
};
43
44
disposables.add(cts.token.onCancellationRequested(() => resolve({ supported: true, clicked: false })));
45
46
Event.once(toast.onClick)(() => resolve({ supported: true, clicked: true }));
47
Event.once(toast.onClose)(() => resolve({ supported: true, clicked: false }));
48
Event.once(toast.onError)(() => resolve({ supported: false, clicked: false }));
49
});
50
}
51
52
interface INotification extends IDisposable {
53
readonly onClick: Event<void>;
54
readonly onClose: Event<void>;
55
readonly onError: Event<void>;
56
}
57
58
async function triggerBrowserToast(message: string, options?: { detail?: string; sticky?: boolean }): Promise<INotification | undefined> {
59
const permission = await Notification.requestPermission();
60
if (permission !== 'granted') {
61
return;
62
}
63
64
const disposables = new DisposableStore();
65
66
const notification = new Notification(message, {
67
body: options?.detail,
68
requireInteraction: options?.sticky,
69
});
70
71
const onClick = disposables.add(new Emitter<void>());
72
const onClose = disposables.add(new Emitter<void>());
73
const onError = disposables.add(new Emitter<void>());
74
75
disposables.add(addDisposableListener(notification, 'click', () => onClick.fire()));
76
disposables.add(addDisposableListener(notification, 'close', () => onClose.fire()));
77
disposables.add(addDisposableListener(notification, 'error', () => onError.fire()));
78
79
disposables.add(toDisposable(() => notification.close()));
80
81
return {
82
onClick: onClick.event,
83
onClose: onClose.event,
84
onError: onError.event,
85
dispose: () => disposables.dispose()
86
};
87
}
88
89