Path: blob/main/src/vs/workbench/services/host/browser/toasts.ts
5241 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { addDisposableListener } from '../../../../base/browser/dom.js';6import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';7import { Emitter, Event } from '../../../../base/common/event.js';8import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';9import { IToastOptions, IToastResult } from './host.js';1011export interface IShowToastController {12onDidCreateToast: (toast: IDisposable) => void;13onDidDisposeToast: (toast: IDisposable) => void;14}1516export async function showBrowserToast(controller: IShowToastController, options: IToastOptions, token: CancellationToken): Promise<IToastResult> {17const toast = await triggerBrowserToast(options.title, {18detail: options.body,19sticky: !options.silent20});2122if (!toast) {23return { supported: false, clicked: false };24}2526const disposables = new DisposableStore();27controller.onDidCreateToast(toast);2829const cts = new CancellationTokenSource(token);3031disposables.add(toDisposable(() => {32controller.onDidDisposeToast(toast);33toast.dispose();34cts.dispose(true);35}));3637return new Promise<IToastResult>(r => {38const resolve = (result: IToastResult) => {39r(result); // first return the result before...40disposables.dispose(); // ...disposing which would invalidate the result object41};4243disposables.add(cts.token.onCancellationRequested(() => resolve({ supported: true, clicked: false })));4445Event.once(toast.onClick)(() => resolve({ supported: true, clicked: true }));46Event.once(toast.onClose)(() => resolve({ supported: true, clicked: false }));47Event.once(toast.onError)(() => resolve({ supported: false, clicked: false }));48});49}5051interface INotification extends IDisposable {52readonly onClick: Event<void>;53readonly onClose: Event<void>;54readonly onError: Event<void>;55}5657async function triggerBrowserToast(message: string, options?: { detail?: string; sticky?: boolean }): Promise<INotification | undefined> {58const permission = await Notification.requestPermission();59if (permission !== 'granted') {60return;61}6263const disposables = new DisposableStore();6465const notification = new Notification(message, {66body: options?.detail,67requireInteraction: options?.sticky,68});6970const onClick = disposables.add(new Emitter<void>());71const onClose = disposables.add(new Emitter<void>());72const onError = disposables.add(new Emitter<void>());7374disposables.add(addDisposableListener(notification, 'click', () => onClick.fire()));75disposables.add(addDisposableListener(notification, 'close', () => onClose.fire()));76disposables.add(addDisposableListener(notification, 'error', () => onError.fire()));7778disposables.add(toDisposable(() => notification.close()));7980return {81onClick: onClick.event,82onClose: onClose.event,83onError: onError.event,84dispose: () => disposables.dispose()85};86}878889