1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 20:24:01 +00:00

initial work

This commit is contained in:
Patrick Pimentel
2025-05-29 10:38:03 -04:00
parent b48356228c
commit 04748b65d8
3 changed files with 158 additions and 0 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}