1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-27 18:13:29 +00:00

fix(notification-processing): [PM-19877] System Notification Implementation - Addressed more feedback.

This commit is contained in:
Patrick Pimentel
2025-08-08 13:10:28 -04:00
parent 81281a2b6a
commit 49bef8ee3c
49 changed files with 112 additions and 140 deletions

View File

@@ -134,7 +134,7 @@ describe("NotificationsService", () => {
expect(actualNotification.type).toBe(expectedType);
};
it("emits notifications through WebPush when supported", async () => {
it("emits server notifications through WebPush when supported", async () => {
const notificationsPromise = firstValueFrom(sut.notifications$.pipe(bufferCount(2)));
emitActiveUser(mockUser1);
@@ -227,7 +227,7 @@ describe("NotificationsService", () => {
});
it.each([
// Temporarily rolling back notifications being connected while locked
// Temporarily rolling back server notifications being connected while locked
// { initialStatus: AuthenticationStatus.Locked, updatedStatus: AuthenticationStatus.Unlocked },
// { initialStatus: AuthenticationStatus.Unlocked, updatedStatus: AuthenticationStatus.Locked },
// { initialStatus: AuthenticationStatus.Locked, updatedStatus: AuthenticationStatus.Locked },
@@ -256,7 +256,7 @@ describe("NotificationsService", () => {
);
it.each([
// Temporarily disabling notifications connecting while in a locked state
// Temporarily disabling server notifications connecting while in a locked state
// AuthenticationStatus.Locked,
AuthenticationStatus.Unlocked,
])(
@@ -282,7 +282,7 @@ describe("NotificationsService", () => {
},
);
it("does not connect to any notification stream when notifications are disabled through special url", () => {
it("does not connect to any notification stream when server notifications are disabled through special url", () => {
const subscription = sut.notifications$.subscribe();
emitActiveUser(mockUser1);
emitNotificationUrl(DISABLED_NOTIFICATIONS_URL);

View File

@@ -61,7 +61,7 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
distinctUntilChanged(),
switchMap((activeAccountId) => {
if (activeAccountId == null) {
// We don't emit notifications for inactive accounts currently
// We don't emit server-notifications for inactive accounts currently
return EMPTY;
}
@@ -74,8 +74,8 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
}
/**
* Retrieves a stream of push notifications for the given user.
* @param userId The user id of the user to get the push notifications for.
* Retrieves a stream of push server notifications for the given user.
* @param userId The user id of the user to get the push server notifications for.
*/
private userNotifications$(userId: UserId) {
return this.environmentService.environment$.pipe(
@@ -109,7 +109,7 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
}),
supportSwitch({
supported: (service) => {
this.logService.info("Using WebPush for notifications");
this.logService.info("Using WebPush for server notifications");
return service.notifications$.pipe(
catchError((err: unknown) => {
this.logService.warning("Issue with web push, falling back to SignalR", err);
@@ -118,7 +118,7 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
);
},
notSupported: () => {
this.logService.info("Using SignalR for notifications");
this.logService.info("Using SignalR for server notifications");
return this.connectSignalR$(userId, notificationsUrl);
},
}),
@@ -236,7 +236,8 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
mergeMap(async ([notification, userId]) => this.processNotification(notification, userId)),
)
.subscribe({
error: (e: unknown) => this.logService.warning("Error in notifications$ observable", e),
error: (e: unknown) =>
this.logService.warning("Error in server notifications$ observable", e),
});
}

View File

@@ -1,7 +1,7 @@
export * from "./worker-webpush-connection.service";
export * from "./signalr-connection.service";
export * from "./default-server-notifications.service";
export * from "./unsupported-server-notifications.service";
export * from "./noop-server-notifications.service";
export * from "./unsupported-webpush-connection.service";
export * from "./webpush-connection.service";
export * from "./websocket-webpush-connection.service";

View File

@@ -6,14 +6,14 @@ import { UserId } from "@bitwarden/common/types/guid";
import { LogService } from "../../abstractions/log.service";
import { ServerNotificationsService } from "../server-notifications.service";
export class UnsupportedServerNotificationsService implements ServerNotificationsService {
export class NoopServerNotificationsService implements ServerNotificationsService {
notifications$: Observable<readonly [NotificationResponse, UserId]> = new Subject();
constructor(private logService: LogService) {}
startListening(): Subscription {
this.logService.info(
"Initializing no-op notification service, no push notifications will be received",
"Initializing no-op notification service, no push server notifications will be received",
);
return Subscription.EMPTY;
}

View File

@@ -10,7 +10,7 @@ export class WebPushNotificationsApiService {
) {}
/**
* Posts a device-user association to the server and ensures it's installed for push notifications
* Posts a device-user association to the server and ensures it's installed for push server notifications
*/
async putSubscription(pushSubscription: PushSubscriptionJSON): Promise<void> {
const request = WebPushRequest.from(pushSubscription);

View File

@@ -40,7 +40,7 @@ interface PushEvent {
}
/**
* An implementation for connecting to web push based notifications running in a Worker.
* An implementation for connecting to web push based server notifications running in a Worker.
*/
export class WorkerWebPushConnectionService implements WebPushConnectionService {
private pushEvent = new Subject<PushEvent>();
@@ -75,7 +75,7 @@ export class WorkerWebPushConnectionService implements WebPushConnectionService
}
supportStatus$(userId: UserId): Observable<SupportStatus<WebPushConnector>> {
// Check the server config to see if it supports sending WebPush notifications
// Check the server config to see if it supports sending WebPush server notifications
// FIXME: get config of server for the specified userId, once ConfigService supports it
return this.configService.serverConfig$.pipe(
map((config) =>

View File

@@ -13,11 +13,11 @@ export abstract class ServerNotificationsService {
/**
* @deprecated This method should not be consumed, an observable to listen to server
* notifications will be available one day but it is not ready to be consumed generally.
* Please add code reacting to notifications in {@link DefaultServerNotificationsService.processNotification}
* Please add code reacting to server notifications in {@link DefaultServerNotificationsService.processNotification}
*/
abstract notifications$: Observable<readonly [NotificationResponse, UserId]>;
/**
* Starts automatic listening and processing of notifications, should only be called once per application,
* Starts automatic listening and processing of server notifications, should only be called once per application,
* or you will risk notifications being processed multiple times.
*/
abstract startListening(): Subscription;

View File

@@ -230,7 +230,7 @@ export abstract class CoreSyncService implements SyncService {
}),
),
);
// Process only notifications for currently active user when user is not logged out
// Process only server notifications for currently active user when user is not logged out
// TODO: once send service allows data manipulation of non-active users, this should process any received notification
if (activeUserId === notification.userId && status !== AuthenticationStatus.LoggedOut) {
try {

View File

@@ -0,0 +1 @@
export { SystemNotificationsService } from "./system-notifications.service";

View File

@@ -32,7 +32,7 @@ export type SystemNotificationEvent = {
};
/**
* A service responsible for displaying operating system level notifications.
* A service responsible for displaying operating system level server notifications.
*/
export abstract class SystemNotificationsService {
abstract notificationClicked$: Observable<SystemNotificationEvent>;
@@ -43,7 +43,7 @@ export abstract class SystemNotificationsService {
* @returns If a notification is successfully created it will respond back with an
* id that refers to a notification.
*/
abstract create(createInfo: SystemNotificationCreateInfo): Promise<string | undefined>;
abstract create(createInfo: SystemNotificationCreateInfo): Promise<string>;
/**
* Clears a notification.
@@ -52,7 +52,7 @@ export abstract class SystemNotificationsService {
abstract clear(clearInfo: SystemNotificationClearInfo): Promise<void>;
/**
* Used to know if a given platform supports notifications.
* Used to know if a given platform supports server notifications.
*/
abstract isSupported(): boolean;
}

View File

@@ -9,7 +9,7 @@ import {
export class UnsupportedSystemNotificationsService implements SystemNotificationsService {
notificationClicked$ = throwError(() => new Error("Notification clicked is not supported."));
async create(createInfo: SystemNotificationCreateInfo): Promise<undefined> {
async create(createInfo: SystemNotificationCreateInfo): Promise<string> {
throw new Error("Create OS Notification unsupported.");
}