mirror of
https://github.com/bitwarden/browser
synced 2026-02-07 20:24:01 +00:00
initial work
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
import { DeviceType } from "@bitwarden/common/enums";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import {
|
||||
ButtonLocation,
|
||||
SystemNotificationClearInfo,
|
||||
SystemNotificationCreateInfo,
|
||||
SystemNotificationEvent,
|
||||
SystemNotificationService,
|
||||
} from "@bitwarden/common/platform/notifications/system-notification-service";
|
||||
|
||||
export class BrowserSystemNotificationService implements SystemNotificationService {
|
||||
private systemNotificationClickedSubject = new Subject<SystemNotificationEvent>();
|
||||
notificationClicked$: Observable<SystemNotificationEvent>;
|
||||
|
||||
constructor(
|
||||
private logService: LogService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
) {
|
||||
this.notificationClicked$ = this.systemNotificationClickedSubject.asObservable();
|
||||
}
|
||||
|
||||
async create(createInfo: SystemNotificationCreateInfo): Promise<undefined> {
|
||||
if (!this.isSupported()) {
|
||||
this.logService.error("While trying to create, found that it is not supported");
|
||||
}
|
||||
|
||||
chrome.notifications.create(createInfo.id, {
|
||||
iconUrl: "https://avatars.githubusercontent.com/u/15990069?s=200",
|
||||
message: createInfo.title,
|
||||
type: "basic",
|
||||
title: createInfo.title,
|
||||
buttons: createInfo.buttons.map((value) => {
|
||||
return { title: value.title };
|
||||
}),
|
||||
});
|
||||
|
||||
// ESLint: Using addListener in the browser popup produces a memory leak in Safari, use `BrowserApi. addListener` instead (no-restricted-syntax)
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chrome.notifications.onButtonClicked.addListener(
|
||||
(notificationId: string, buttonIndex: number) => {
|
||||
this.systemNotificationClickedSubject.next({
|
||||
id: notificationId,
|
||||
type: createInfo.type,
|
||||
buttonIdentifier: buttonIndex,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chrome.notifications.onClicked.addListener((notificationId: string) => {
|
||||
this.systemNotificationClickedSubject.next({
|
||||
id: notificationId,
|
||||
type: createInfo.type,
|
||||
buttonIdentifier: ButtonLocation.NotificationButton,
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
clear(clearInfo: SystemNotificationClearInfo): undefined {
|
||||
if (!this.isSupported()) {
|
||||
this.logService.error("While trying to clear, found that it is not supported");
|
||||
}
|
||||
|
||||
chrome.notifications.clear(clearInfo.id);
|
||||
}
|
||||
|
||||
isSupported(): boolean {
|
||||
switch (this.platformUtilsService.getDevice()) {
|
||||
case DeviceType.EdgeExtension:
|
||||
case DeviceType.VivaldiExtension:
|
||||
case DeviceType.OperaExtension:
|
||||
case DeviceType.ChromeExtension:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
export const ButtonActions = {
|
||||
AuthRequestNotification: "authRequestNotification",
|
||||
};
|
||||
|
||||
export type ButtonActionsKeys = (typeof ButtonActions)[keyof typeof ButtonActions];
|
||||
|
||||
// This is currently tailored for chrome extension's api, if safari works
|
||||
// differently where clicking a notification button produces a different
|
||||
// identifier we need to reconcile that here.
|
||||
export const ButtonLocation = {
|
||||
FirstOptionalButton: 0, // this is the first optional button we can set
|
||||
SecondOptionalButton: 1, // this is the second optional button we can set
|
||||
NotificationButton: 2, // this is when you click the notification as a whole
|
||||
};
|
||||
|
||||
export type ButtonLocationKeys = (typeof ButtonLocation)[keyof typeof ButtonLocation];
|
||||
|
||||
export type SystemNotificationsButton = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type SystemNotificationCreateInfo = {
|
||||
id: string;
|
||||
type: ButtonActionsKeys;
|
||||
title: string;
|
||||
body: string;
|
||||
buttons: SystemNotificationsButton[];
|
||||
};
|
||||
|
||||
export type SystemNotificationClearInfo = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type SystemNotificationEvent = {
|
||||
id: string;
|
||||
type: string;
|
||||
buttonIdentifier: number;
|
||||
};
|
||||
|
||||
export abstract class SystemNotificationService {
|
||||
abstract notificationClicked$: Observable<SystemNotificationEvent>;
|
||||
abstract create(createInfo: SystemNotificationCreateInfo): Promise<undefined>;
|
||||
abstract clear(clearInfo: SystemNotificationClearInfo): undefined;
|
||||
|
||||
/**
|
||||
* Used to know if a given platform supports notifications.
|
||||
*/
|
||||
abstract isSupported(): boolean;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Subject, throwError } from "rxjs";
|
||||
|
||||
import {
|
||||
SystemNotificationClearInfo,
|
||||
SystemNotificationCreateInfo,
|
||||
SystemNotificationEvent,
|
||||
SystemNotificationService,
|
||||
} from "./system-notification-service";
|
||||
|
||||
export class UnsupportedSystemNotificationService implements SystemNotificationService {
|
||||
private systemNotificationClickedSubject = new Subject<SystemNotificationEvent>();
|
||||
notificationClicked$ = throwError(() => new Error("Notification clicked is not supported."));
|
||||
|
||||
async create(createInfo: SystemNotificationCreateInfo): Promise<undefined> {
|
||||
throw new Error("Create OS Notification unsupported.");
|
||||
}
|
||||
|
||||
clear(clearInfo: SystemNotificationClearInfo): undefined {
|
||||
throw new Error("Clear OS Notification unsupported.");
|
||||
}
|
||||
|
||||
isSupported(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user