diff --git a/apps/browser/src/platform/notifications/foreground-notifications.service.ts b/apps/browser/src/platform/notifications/foreground-notifications.service.ts new file mode 100644 index 00000000000..e7685d3fe50 --- /dev/null +++ b/apps/browser/src/platform/notifications/foreground-notifications.service.ts @@ -0,0 +1,31 @@ +import { Observable, Subscription } from "rxjs"; + +import { NotificationResponse } from "@bitwarden/common/models/response/notification.response"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { NotificationsService } from "@bitwarden/common/platform/notifications"; +import { UserId } from "@bitwarden/common/types/guid"; + +// Eventually if we want to support listening to notifications from browser foreground we +// will only ever create a single SignalR connection, likely messaging to the background to reuse its connection. +export class ForegroundNotificationsService implements NotificationsService { + notifications$: Observable; + + constructor(private readonly logService: LogService) { + this.notifications$ = new Observable((subscriber) => { + this.logService.warning( + "Notifications will never emit from browser foreground, you will need to listen to messages from `DefaultNotificationsService.processNotification`", + ); + subscriber.complete(); + }); + } + + startListening(): Subscription { + throw new Error("startListening should never be called from browser foreground."); + } + reconnectFromActivity(): void { + throw new Error("Activity should not be managed from browser foreground."); + } + disconnectFromInactivity(): void { + throw new Error("Activity should not be managed from browser foreground."); + } +} diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 26e84ea964c..1c907f18f83 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -94,6 +94,7 @@ import { Message, MessageListener, MessageSender } from "@bitwarden/common/platf // eslint-disable-next-line no-restricted-imports -- Used for dependency injection import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; import { flagEnabled } from "@bitwarden/common/platform/misc/flags"; +import { NotificationsService } from "@bitwarden/common/platform/notifications"; import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; @@ -157,6 +158,7 @@ import { runInsideAngular } from "../../platform/browser/run-inside-angular.oper import { ZonedMessageListenerService } from "../../platform/browser/zoned-message-listener.service"; import { ChromeMessageSender } from "../../platform/messaging/chrome-message.sender"; /* eslint-enable no-restricted-imports */ +import { ForegroundNotificationsService } from "../../platform/notifications/foreground-notifications.service"; import { OffscreenDocumentService } from "../../platform/offscreen-document/abstractions/offscreen-document"; import { DefaultOffscreenDocumentService } from "../../platform/offscreen-document/offscreen-document.service"; import { PopupCompactModeService } from "../../platform/popup/layout/popup-compact-mode.service"; @@ -660,6 +662,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultSshImportPromptService, deps: [DialogService, ToastService, PlatformUtilsService, I18nServiceAbstraction], }), + safeProvider({ + provide: NotificationsService, + useClass: ForegroundNotificationsService, + deps: [LogService], + }), ]; @NgModule({ diff --git a/libs/common/src/platform/notifications/internal/default-notifications.service.ts b/libs/common/src/platform/notifications/internal/default-notifications.service.ts index fc505b018ce..423b3370455 100644 --- a/libs/common/src/platform/notifications/internal/default-notifications.service.ts +++ b/libs/common/src/platform/notifications/internal/default-notifications.service.ts @@ -7,6 +7,7 @@ import { map, mergeMap, Observable, + share, switchMap, } from "rxjs"; @@ -66,6 +67,7 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract map((notification) => [notification, activeAccountId] as const), ); }), + share(), // Multiple subscribers should only create a single connection to the server ); } diff --git a/libs/common/src/platform/notifications/notifications.service.ts b/libs/common/src/platform/notifications/notifications.service.ts index 2adc66e361f..9e72b4de493 100644 --- a/libs/common/src/platform/notifications/notifications.service.ts +++ b/libs/common/src/platform/notifications/notifications.service.ts @@ -3,10 +3,18 @@ import { Observable, Subscription } from "rxjs"; import { NotificationResponse } from "@bitwarden/common/models/response/notification.response"; import { UserId } from "@bitwarden/common/types/guid"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars -- Needed to link to API +import type { DefaultNotificationsService } from "./internal"; + /** * A service offering abilities to interact with push notifications from the server. */ export abstract class NotificationsService { + /** + * @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 DefaultNotificationsService.processNotification} + */ abstract notifications$: Observable; /** * Starts automatic listening and processing of notifications, should only be called once per application,