1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-06 11:43:51 +00:00

feat(notification-processing): [PM-19877] System Notification Implementation - Giving up on safari. Getting the code ready for review now.

This commit is contained in:
Patrick Pimentel
2025-07-21 12:41:23 -04:00
parent b269eaed0b
commit 50621218d4
5 changed files with 71 additions and 19 deletions

View File

@@ -303,7 +303,7 @@ import { BackgroundMemoryStorageService } from "../platform/storage/background-m
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
import { OffscreenStorageService } from "../platform/storage/offscreen-storage.service";
import { SyncServiceListener } from "../platform/sync/sync-service.listener";
import { ChromeExtensionSystemNotificationService } from "../platform/system-notifications/chrome-extension-system-notification.service";
import { BrowserSystemNotificationService } from "../platform/system-notifications/browser-system-notification.service";
import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
import { VaultFilterService } from "../vault/services/vault-filter.service";
@@ -1132,10 +1132,16 @@ export default class MainBackground {
this.webPushConnectionService = new UnsupportedWebPushConnectionService();
}
this.logService.info(`The background service is registered as ${navigator.userAgent}`);
this.logService.info(
`The background service is registered as ${navigator.userAgent} with manifest version ${BrowserApi.manifestVersion}`,
);
this.actionsService = new BrowserActionsService(this.logService, this.platformUtilsService);
setTimeout(async () => {
await this.actionsService.openPopup();
}, 1);
const userAgent = navigator.userAgent;
const isChrome =
@@ -1144,7 +1150,7 @@ export default class MainBackground {
const isFirefox = userAgent.includes("Firefox");
if ((isChrome || isFirefox) && !isSafari) {
this.systemNotificationService = new ChromeExtensionSystemNotificationService(
this.systemNotificationService = new BrowserSystemNotificationService(
this.logService,
this.platformUtilsService,
);
@@ -1153,17 +1159,15 @@ export default class MainBackground {
}
setTimeout(async () => {
this.logService.info("CREATING NOTIFICATION");
await this.systemNotificationService.create({
id: Math.random() * 100000 + "",
type: ButtonActions.AuthRequestNotification,
title: "Test Notification",
body: "Body",
buttons: [
{
title: "First Button",
},
],
buttons: [],
});
this.logService.info("DONE CREATING NOTIFICATION");
}, 1);
this.notificationsService = new DefaultNotificationsService(

View File

@@ -11,7 +11,7 @@ import {
SystemNotificationsService,
} from "@bitwarden/common/platform/notifications/system-notifications-service";
export class ChromeExtensionSystemNotificationService implements SystemNotificationsService {
export class BrowserSystemNotificationService implements SystemNotificationsService {
private systemNotificationClickedSubject = new Subject<SystemNotificationEvent>();
notificationClicked$: Observable<SystemNotificationEvent>;
@@ -58,10 +58,37 @@ export class ChromeExtensionSystemNotificationService implements SystemNotificat
});
break;
case DeviceType.FirefoxExtension:
this.logService.info("Creating firefox notification");
await browser.notifications.create(createInfo.id, {
iconUrl: "https://avatars.githubusercontent.com/u/15990069?s=200",
message: createInfo.title,
type: "basic",
title: createInfo.title,
});
browser.notifications.onButtonClicked.addListener(
(notificationId: string, buttonIndex: number) => {
this.systemNotificationClickedSubject.next({
id: notificationId,
type: createInfo.type,
buttonIdentifier: buttonIndex,
});
},
);
browser.notifications.onClicked.addListener((notificationId: string) => {
this.systemNotificationClickedSubject.next({
id: notificationId,
type: createInfo.type,
buttonIdentifier: ButtonLocation.NotificationButton,
});
});
}
}
clear(clearInfo: SystemNotificationClearInfo): undefined {
async clear(clearInfo: SystemNotificationClearInfo): Promise<undefined> {
chrome.notifications.clear(clearInfo.id);
}

View File

@@ -184,7 +184,7 @@ import { ForegroundTaskSchedulerService } from "../../platform/services/task-sch
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
import { ForegroundSyncService } from "../../platform/sync/foreground-sync.service";
import { ChromeExtensionSystemNotificationService } from "../../platform/system-notifications/chrome-extension-system-notification.service";
import { BrowserSystemNotificationService } from "../../platform/system-notifications/browser-system-notification.service";
import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging";
import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service";
import { Fido2UserVerificationService } from "../../vault/services/fido2-user-verification.service";
@@ -617,7 +617,7 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: SystemNotificationsService,
useClass: ChromeExtensionSystemNotificationService,
useClass: BrowserSystemNotificationService,
deps: [LogService, PlatformUtilsServiceAbstraction],
}),
safeProvider({

View File

@@ -17,6 +17,7 @@ import { LogoutReason } from "@bitwarden/auth/common";
import { ActionsService } from "@bitwarden/common/platform/actions";
import {
ButtonActions,
ButtonLocation,
SystemNotificationEvent,
SystemNotificationsService,
} from "@bitwarden/common/platform/notifications/system-notifications-service";
@@ -66,12 +67,12 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract
) {
this.systemNotificationService.notificationClicked$
.pipe(
map(async (value: SystemNotificationEvent) => {
switch (value.type) {
case ButtonActions.AuthRequestNotification:
await this.actionService.openPopup();
}
}),
filter(
(event: SystemNotificationEvent) => event.type === ButtonActions.AuthRequestNotification,
),
mergeMap((event: SystemNotificationEvent) =>
this.handleAuthRequestNotificationClicked(event),
),
)
.subscribe();
@@ -92,6 +93,26 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract
);
}
/**
* TODO: Requests are doubling, figure out if a subscription is duplicating or something.
* @param event
* @private
*/
private async handleAuthRequestNotificationClicked(
event: SystemNotificationEvent,
): Promise<void> {
// This is the approval event. WE WILL NOT BE USING 0 or 1!
if (event.buttonIdentifier === ButtonLocation.FirstOptionalButton) {
this.logService.info("Approve the request");
} else if (event.buttonIdentifier === ButtonLocation.SecondOptionalButton) {
// This is the deny event.
this.logService.info("Deny the request");
} else if (event.buttonIdentifier === ButtonLocation.NotificationButton) {
this.logService.info("Main button clicked, open popup");
await this.actionService.openPopup();
}
}
/**
* Retrieves a stream of push notifications for the given user.
* @param userId The user id of the user to get the push notifications for.

View File

@@ -42,7 +42,7 @@ export type SystemNotificationEvent = {
export abstract class SystemNotificationsService {
abstract notificationClicked$: Observable<SystemNotificationEvent>;
abstract create(createInfo: SystemNotificationCreateInfo): Promise<undefined>;
abstract clear(clearInfo: SystemNotificationClearInfo): undefined;
abstract clear(clearInfo: SystemNotificationClearInfo): Promise<void>;
/**
* Used to know if a given platform supports notifications.