mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 03:33:30 +00:00
feat(notification-processing): [PM-19877] System Notification Implementation - Initial changeset
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
VaultTimeoutSettingsService,
|
||||
VaultTimeoutStringType,
|
||||
} from "@bitwarden/common/key-management/vault-timeout";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
|
||||
const IdleInterval = 60 * 5; // 5 minutes
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class IdleBackground {
|
||||
|
||||
constructor(
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notificationsService: ServerNotificationsService,
|
||||
private accountService: AccountService,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
) {
|
||||
|
||||
@@ -111,6 +111,7 @@ import {
|
||||
ObservableStorageService,
|
||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
|
||||
import { ActionsService } from "@bitwarden/common/platform/actions/actions-service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { IpcService } from "@bitwarden/common/platform/ipc";
|
||||
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
@@ -120,7 +121,7 @@ import { Lazy } from "@bitwarden/common/platform/misc/lazy";
|
||||
import { Account } from "@bitwarden/common/platform/models/domain/account";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
|
||||
import {
|
||||
DefaultNotificationsService,
|
||||
@@ -129,6 +130,8 @@ import {
|
||||
WebPushNotificationsApiService,
|
||||
WorkerWebPushConnectionService,
|
||||
} from "@bitwarden/common/platform/notifications/internal";
|
||||
import { SystemNotificationService } from "@bitwarden/common/platform/notifications/system-notification-service";
|
||||
import { UnsupportedSystemNotificationService } from "@bitwarden/common/platform/notifications/unsupported-system-notification.service";
|
||||
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
|
||||
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
|
||||
import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service";
|
||||
@@ -268,6 +271,7 @@ import { InlineMenuFieldQualificationService } from "../autofill/services/inline
|
||||
import { SafariApp } from "../browser/safariApp";
|
||||
import { BackgroundBrowserBiometricsService } from "../key-management/biometrics/background-browser-biometrics.service";
|
||||
import VaultTimeoutService from "../key-management/vault-timeout/vault-timeout.service";
|
||||
import { BrowserActionsService } from "../platform/actions/browser-actions.service";
|
||||
import { DefaultBadgeBrowserApi } from "../platform/badge/badge-browser-api";
|
||||
import { BadgeService } from "../platform/badge/badge.service";
|
||||
import { BrowserApi } from "../platform/browser/browser-api";
|
||||
@@ -296,6 +300,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 { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
|
||||
import { VaultFilterService } from "../vault/services/vault-filter.service";
|
||||
|
||||
@@ -341,7 +346,9 @@ export default class MainBackground {
|
||||
importService: ImportServiceAbstraction;
|
||||
exportService: VaultExportServiceAbstraction;
|
||||
searchService: SearchServiceAbstraction;
|
||||
notificationsService: NotificationsService;
|
||||
notificationsService: ServerNotificationsService;
|
||||
systemNotificationService: SystemNotificationService;
|
||||
actionsService: ActionsService;
|
||||
stateService: StateServiceAbstraction;
|
||||
userNotificationSettingsService: UserNotificationSettingsServiceAbstraction;
|
||||
autofillSettingsService: AutofillSettingsServiceAbstraction;
|
||||
@@ -1122,6 +1129,24 @@ export default class MainBackground {
|
||||
this.webPushConnectionService = new UnsupportedWebPushConnectionService();
|
||||
}
|
||||
|
||||
this.actionsService = new BrowserActionsService(this.platformUtilsService);
|
||||
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
const isChrome =
|
||||
userAgent.includes("Chrome") && !userAgent.includes("Edge") && !userAgent.includes("OPR");
|
||||
const isSafari = userAgent.includes("Safari") && !userAgent.includes("Chrome");
|
||||
const isFirefox = userAgent.includes("Firefox");
|
||||
|
||||
if ((isChrome || isFirefox) && !isSafari) {
|
||||
this.systemNotificationService = new ChromeExtensionSystemNotificationService(
|
||||
this.logService,
|
||||
this.platformUtilsService,
|
||||
);
|
||||
} else {
|
||||
this.systemNotificationService = new UnsupportedSystemNotificationService();
|
||||
}
|
||||
|
||||
this.notificationsService = new DefaultNotificationsService(
|
||||
this.logService,
|
||||
this.syncService,
|
||||
@@ -1690,6 +1715,11 @@ export default class MainBackground {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the popup.
|
||||
*
|
||||
* @deprecated Migrating to the browser actions service.
|
||||
*/
|
||||
async openPopup() {
|
||||
const browserAction = BrowserApi.getBrowserAction();
|
||||
|
||||
@@ -1726,6 +1756,7 @@ export default class MainBackground {
|
||||
/**
|
||||
* Opens the popup to the given page
|
||||
* @default ExtensionPageUrls.Index
|
||||
* @deprecated Migrating to the browser actions service.
|
||||
*/
|
||||
async openTheExtensionToPage(url: ExtensionPageUrls = ExtensionPageUrls.Index) {
|
||||
const isValidUrl = Object.values(ExtensionPageUrls).includes(url);
|
||||
|
||||
@@ -15,7 +15,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { MessageListener, isExternalMessage } from "@bitwarden/common/platform/messaging";
|
||||
import { devFlagEnabled } from "@bitwarden/common/platform/misc/flags";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum";
|
||||
import { BiometricsCommands } from "@bitwarden/key-management";
|
||||
@@ -46,7 +46,7 @@ export default class RuntimeBackground {
|
||||
private main: MainBackground,
|
||||
private autofillService: AutofillService,
|
||||
private platformUtilsService: BrowserPlatformUtilsService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notificationsService: ServerNotificationsService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private processReloadSerivce: ProcessReloadServiceAbstraction,
|
||||
private environmentService: BrowserEnvironmentService,
|
||||
@@ -424,6 +424,12 @@ export default class RuntimeBackground {
|
||||
return await BrowserApi.tabsQuery({ url: `${urlObj.href}*` });
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the popup.
|
||||
*
|
||||
* Migrating to the browser actions service, not deprecating yet.
|
||||
* @private
|
||||
*/
|
||||
private async openPopup() {
|
||||
await this.main.openPopup();
|
||||
}
|
||||
@@ -450,7 +456,7 @@ export default class RuntimeBackground {
|
||||
/** Sends a message to each tab that the popup was opened */
|
||||
private announcePopupOpen() {
|
||||
const announceToAllTabs = async () => {
|
||||
const isOpen = await this.platformUtilsService.isViewOpen();
|
||||
const isOpen = await this.platformUtilsService.isPopupOpen();
|
||||
const tabs = await this.getBwTabs();
|
||||
|
||||
if (isOpen && tabs.length > 0) {
|
||||
|
||||
26
apps/browser/src/platform/actions/browser-actions.service.ts
Normal file
26
apps/browser/src/platform/actions/browser-actions.service.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { DeviceType } from "@bitwarden/common/enums";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ActionsService } from "@bitwarden/common/platform/actions/actions-service";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
|
||||
export class BrowserActionsService implements ActionsService {
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
|
||||
async openPopup(): Promise<void> {
|
||||
switch (this.platformUtilsService.getDevice()) {
|
||||
case DeviceType.ChromeBrowser:
|
||||
case DeviceType.ChromeExtension: {
|
||||
const browserAction = BrowserApi.getBrowserAction();
|
||||
|
||||
if ("openPopup" in browserAction && typeof browserAction.openPopup === "function") {
|
||||
await browserAction.openPopup();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceType.SafariBrowser:
|
||||
case DeviceType.SafariExtension:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@ 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 { ServerNotificationsService } 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 {
|
||||
export class ForegroundServerNotificationsService implements ServerNotificationsService {
|
||||
notifications$: Observable<readonly [NotificationResponse, UserId]>;
|
||||
|
||||
constructor(private readonly logService: LogService) {
|
||||
@@ -150,7 +150,7 @@ describe("Browser Utils Service", () => {
|
||||
callback(undefined);
|
||||
});
|
||||
|
||||
const isViewOpen = await browserPlatformUtilsService.isViewOpen();
|
||||
const isViewOpen = await browserPlatformUtilsService.isPopupOpen();
|
||||
|
||||
expect(isViewOpen).toBe(false);
|
||||
});
|
||||
@@ -160,7 +160,7 @@ describe("Browser Utils Service", () => {
|
||||
callback(message.command === "checkVaultPopupHeartbeat");
|
||||
});
|
||||
|
||||
const isViewOpen = await browserPlatformUtilsService.isViewOpen();
|
||||
const isViewOpen = await browserPlatformUtilsService.isPopupOpen();
|
||||
|
||||
expect(isViewOpen).toBe(true);
|
||||
});
|
||||
@@ -173,7 +173,7 @@ describe("Browser Utils Service", () => {
|
||||
callback(undefined);
|
||||
});
|
||||
|
||||
const isViewOpen = await browserPlatformUtilsService.isViewOpen();
|
||||
const isViewOpen = await browserPlatformUtilsService.isPopupOpen();
|
||||
|
||||
expect(isViewOpen).toBe(false);
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
|
||||
* message to the popup and waiting for a response. If a response is received,
|
||||
* the view is open.
|
||||
*/
|
||||
async isViewOpen(): Promise<boolean> {
|
||||
async isPopupOpen(): Promise<boolean> {
|
||||
if (this.isSafari()) {
|
||||
// Query views on safari since chrome.runtime.sendMessage does not timeout and will hang.
|
||||
return BrowserApi.isPopupOpen();
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
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,79 @@
|
||||
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 ChromeExtensionSystemNotificationService 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> {
|
||||
switch (this.platformUtilsService.getDevice()) {
|
||||
case DeviceType.ChromeExtension:
|
||||
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,
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clear(clearInfo: SystemNotificationClearInfo): undefined {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,10 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
|
||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import {
|
||||
PlatformUtilsService as PlatformUtilsServiceAbstraction,
|
||||
PlatformUtilsService,
|
||||
} from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
|
||||
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
|
||||
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
||||
@@ -92,7 +95,8 @@ 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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { SystemNotificationService } from "@bitwarden/common/platform/notifications/system-notification-service";
|
||||
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";
|
||||
@@ -160,7 +164,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 { ForegroundServerNotificationsService } from "../../platform/notifications/foreground-server-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";
|
||||
@@ -178,6 +182,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 { 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";
|
||||
@@ -603,6 +608,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: SsoUrlService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SystemNotificationService,
|
||||
useClass: ChromeExtensionSystemNotificationService,
|
||||
deps: [LogService, PlatformUtilsServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LoginComponentService,
|
||||
useClass: ExtensionLoginComponentService,
|
||||
@@ -663,8 +673,8 @@ const safeProviders: SafeProvider[] = [
|
||||
deps: [DialogService, ToastService, PlatformUtilsService, I18nServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: NotificationsService,
|
||||
useClass: ForegroundNotificationsService,
|
||||
provide: ServerNotificationsService,
|
||||
useClass: ForegroundServerNotificationsService,
|
||||
deps: [LogService],
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -75,7 +75,7 @@ export class CliPlatformUtilsService implements PlatformUtilsService {
|
||||
return false;
|
||||
}
|
||||
|
||||
isViewOpen() {
|
||||
isPopupOpen() {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SystemService } from "@bitwarden/common/platform/abstractions/system.service";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { StateEventRunnerService } from "@bitwarden/common/platform/state";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
@@ -152,7 +152,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private messagingService: MessagingService,
|
||||
private collectionService: CollectionService,
|
||||
private searchService: SearchService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notificationsService: ServerNotificationsService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private systemService: SystemService,
|
||||
private processReloadService: ProcessReloadServiceAbstraction,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/platform/sync";
|
||||
@@ -38,7 +38,7 @@ export class InitService {
|
||||
private i18nService: I18nServiceAbstraction,
|
||||
private eventUploadService: EventUploadServiceAbstraction,
|
||||
private twoFactorService: TwoFactorServiceAbstraction,
|
||||
private notificationsService: NotificationsService,
|
||||
private notificationsService: ServerNotificationsService,
|
||||
private platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
private stateService: StateServiceAbstraction,
|
||||
private keyService: KeyServiceAbstraction,
|
||||
|
||||
@@ -59,7 +59,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
return ipc.platform.isMacAppStore;
|
||||
}
|
||||
|
||||
isViewOpen(): Promise<boolean> {
|
||||
isPopupOpen(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { StateEventRunnerService } from "@bitwarden/common/platform/state";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
@@ -73,7 +73,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
private keyService: KeyService,
|
||||
private collectionService: CollectionService,
|
||||
private searchService: SearchService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notificationsService: ServerNotificationsService,
|
||||
private stateService: StateService,
|
||||
private eventUploadService: EventUploadService,
|
||||
protected policyListService: PolicyListService,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
|
||||
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { IpcService } from "@bitwarden/common/platform/ipc";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||
@@ -28,7 +28,7 @@ import { VersionService } from "../platform/version.service";
|
||||
export class InitService {
|
||||
constructor(
|
||||
@Inject(WINDOW) private win: Window,
|
||||
private notificationsService: NotificationsService,
|
||||
private notificationsService: ServerNotificationsService,
|
||||
private vaultTimeoutService: DefaultVaultTimeoutService,
|
||||
private i18nService: I18nServiceAbstraction,
|
||||
private eventUploadService: EventUploadServiceAbstraction,
|
||||
|
||||
@@ -98,7 +98,7 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
return false;
|
||||
}
|
||||
|
||||
isViewOpen(): Promise<boolean> {
|
||||
isPopupOpen(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/inter
|
||||
import { devFlagEnabled } from "@bitwarden/common/platform/misc/flags";
|
||||
import { Account } from "@bitwarden/common/platform/models/domain/account";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
|
||||
import {
|
||||
DefaultNotificationsService,
|
||||
@@ -945,7 +945,7 @@ const safeProviders: SafeProvider[] = [
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: NotificationsService,
|
||||
provide: ServerNotificationsService,
|
||||
useClass: devFlagEnabled("noopNotifications")
|
||||
? NoopNotificationsService
|
||||
: DefaultNotificationsService,
|
||||
@@ -1552,7 +1552,7 @@ const safeProviders: SafeProvider[] = [
|
||||
ApiServiceAbstraction,
|
||||
OrganizationServiceAbstraction,
|
||||
AuthServiceAbstraction,
|
||||
NotificationsService,
|
||||
ServerNotificationsService,
|
||||
MessageListener,
|
||||
],
|
||||
}),
|
||||
@@ -1562,7 +1562,7 @@ const safeProviders: SafeProvider[] = [
|
||||
deps: [
|
||||
StateProvider,
|
||||
ApiServiceAbstraction,
|
||||
NotificationsService,
|
||||
ServerNotificationsService,
|
||||
AuthServiceAbstraction,
|
||||
LogService,
|
||||
],
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from "@angular/core";
|
||||
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { ActivatedRoute, Router, RouterModule } from "@angular/router";
|
||||
import { firstValueFrom, Subject, take, takeUntil } from "rxjs";
|
||||
@@ -30,6 +38,10 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import {
|
||||
ButtonActions,
|
||||
SystemNotificationService,
|
||||
} from "@bitwarden/common/platform/notifications/system-notification-service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
@@ -127,10 +139,32 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
private loginSuccessHandlerService: LoginSuccessHandlerService,
|
||||
private masterPasswordService: MasterPasswordServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private systemNotificationService: SystemNotificationService,
|
||||
) {
|
||||
this.clientType = this.platformUtilsService.getClientType();
|
||||
}
|
||||
|
||||
@HostListener("document:keydown.control.b", ["$event"])
|
||||
async onCtrlB(event: KeyboardEvent) {
|
||||
if (process.env.ENV === "development") {
|
||||
event.preventDefault();
|
||||
await this.systemNotificationService.create({
|
||||
id: Math.random() * 10000000 + "",
|
||||
type: ButtonActions.AuthRequestNotification,
|
||||
title: "Test",
|
||||
body: "Body",
|
||||
buttons: [],
|
||||
});
|
||||
}
|
||||
|
||||
// this.systemNotificationService.notificationClicked$.pipe(takeUntilDestroyed()).subscribe((value) => {
|
||||
// if (value == ButtonActions.AuthRequestNotification)
|
||||
// });
|
||||
// this.ngZone.onStable.pipe(take(1), takeUntil(this.destroy$)).subscribe(() => {
|
||||
// this.masterPasswordInputRef?.nativeElement?.focus();
|
||||
// });
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
// Add popstate listener to listen for browser back button clicks
|
||||
window.addEventListener("popstate", this.handlePopState);
|
||||
|
||||
@@ -182,7 +182,7 @@ describe("VaultTimeoutService", () => {
|
||||
),
|
||||
);
|
||||
|
||||
platformUtilsService.isViewOpen.mockResolvedValue(globalSetups?.isViewOpen ?? false);
|
||||
platformUtilsService.isPopupOpen.mockResolvedValue(globalSetups?.isViewOpen ?? false);
|
||||
|
||||
vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$.mockImplementation((userId) => {
|
||||
return new BehaviorSubject<VaultTimeoutAction>(accounts[userId]?.timeoutAction);
|
||||
@@ -222,7 +222,7 @@ describe("VaultTimeoutService", () => {
|
||||
it.each([AuthenticationStatus.Locked, AuthenticationStatus.LoggedOut])(
|
||||
"should not try to log out or lock any user that has authStatus === %s.",
|
||||
async (authStatus) => {
|
||||
platformUtilsService.isViewOpen.mockResolvedValue(false);
|
||||
platformUtilsService.isPopupOpen.mockResolvedValue(false);
|
||||
setupAccounts({
|
||||
1: {
|
||||
authStatus: authStatus,
|
||||
|
||||
@@ -82,7 +82,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||
|
||||
async checkVaultTimeout(): Promise<void> {
|
||||
// Get whether or not the view is open a single time so it can be compared for each user
|
||||
const isViewOpen = await this.platformUtilsService.isViewOpen();
|
||||
const isViewOpen = await this.platformUtilsService.isPopupOpen();
|
||||
|
||||
await firstValueFrom(
|
||||
combineLatest([
|
||||
|
||||
@@ -22,7 +22,7 @@ export abstract class PlatformUtilsService {
|
||||
abstract isVivaldi(): boolean;
|
||||
abstract isSafari(): boolean;
|
||||
abstract isMacAppStore(): boolean;
|
||||
abstract isViewOpen(): Promise<boolean>;
|
||||
abstract isPopupOpen(): Promise<boolean>;
|
||||
abstract launchUri(uri: string, options?: any): void;
|
||||
abstract getApplicationVersion(): Promise<string>;
|
||||
abstract getApplicationVersionNumber(): Promise<string>;
|
||||
|
||||
3
libs/common/src/platform/actions/actions-service.ts
Normal file
3
libs/common/src/platform/actions/actions-service.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export abstract class ActionsService {
|
||||
abstract openPopup(): Promise<void>;
|
||||
}
|
||||
1
libs/common/src/platform/actions/index.ts
Normal file
1
libs/common/src/platform/actions/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { ActionsService } from "./actions-service";
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ActionsService } from "./actions-service";
|
||||
|
||||
export class UnsupportedActionsService implements ActionsService {
|
||||
openPopup(): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
export { NotificationsService } from "./notifications.service";
|
||||
export { ServerNotificationsService } from "./server-notifications-service";
|
||||
|
||||
@@ -32,7 +32,7 @@ import { EnvironmentService } from "../../abstractions/environment.service";
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { MessagingService } from "../../abstractions/messaging.service";
|
||||
import { supportSwitch } from "../../misc/support-status";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "../notifications.service";
|
||||
import { ServerNotificationsService as NotificationsServiceAbstraction } from "../server-notifications-service";
|
||||
|
||||
import { ReceiveMessage, SignalRConnectionService } from "./signalr-connection.service";
|
||||
import { WebPushConnectionService } from "./webpush-connection.service";
|
||||
@@ -188,7 +188,6 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract
|
||||
case NotificationType.SyncCiphers:
|
||||
case NotificationType.SyncSettings:
|
||||
await this.syncService.fullSync(false);
|
||||
|
||||
break;
|
||||
case NotificationType.SyncOrganizations:
|
||||
// An organization update may not have bumped the user's account revision date, so force a sync
|
||||
@@ -214,11 +213,9 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract
|
||||
await this.syncService.syncDeleteSend(notification.payload as SyncSendNotification);
|
||||
break;
|
||||
case NotificationType.AuthRequest:
|
||||
{
|
||||
this.messagingService.send("openLoginApproval", {
|
||||
notificationId: notification.payload.id,
|
||||
});
|
||||
}
|
||||
this.messagingService.send("openLoginApproval", {
|
||||
notificationId: notification.payload.id,
|
||||
});
|
||||
break;
|
||||
case NotificationType.SyncOrganizationStatusChanged:
|
||||
await this.syncService.fullSync(true);
|
||||
|
||||
@@ -4,9 +4,9 @@ import { NotificationResponse } from "@bitwarden/common/models/response/notifica
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { NotificationsService } from "../notifications.service";
|
||||
import { ServerNotificationsService } from "../server-notifications-service";
|
||||
|
||||
export class NoopNotificationsService implements NotificationsService {
|
||||
export class NoopNotificationsService implements ServerNotificationsService {
|
||||
notifications$: Observable<readonly [NotificationResponse, UserId]> = new Subject();
|
||||
|
||||
constructor(private logService: LogService) {}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { DefaultNotificationsService } from "./internal";
|
||||
/**
|
||||
* A service offering abilities to interact with push notifications from the server.
|
||||
*/
|
||||
export abstract class NotificationsService {
|
||||
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.
|
||||
@@ -9,11 +9,11 @@ export type ButtonActionsKeys = (typeof ButtonActions)[keyof typeof ButtonAction
|
||||
// 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 = {
|
||||
export const ButtonLocation = Object.freeze({
|
||||
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];
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { firstValueFrom, of } from "rxjs";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { NotificationId, UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
describe("End User Notification Center Service", () => {
|
||||
let fakeStateProvider: FakeStateProvider;
|
||||
let mockApiService: jest.Mocked<ApiService>;
|
||||
let mockNotificationsService: jest.Mocked<NotificationsService>;
|
||||
let mockNotificationsService: jest.Mocked<ServerNotificationsService>;
|
||||
let mockAuthService: jest.Mocked<AuthService>;
|
||||
let mockLogService: jest.Mocked<LogService>;
|
||||
let service: DefaultEndUserNotificationService;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { NotificationType } from "@bitwarden/common/enums";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { NotificationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
@@ -36,7 +36,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
constructor(
|
||||
private stateProvider: StateProvider,
|
||||
private apiService: ApiService,
|
||||
private notificationService: NotificationsService,
|
||||
private notificationService: ServerNotificationsService,
|
||||
private authService: AuthService,
|
||||
private logService: LogService,
|
||||
) {}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { NotificationType } from "@bitwarden/common/enums";
|
||||
import { NotificationResponse } from "@bitwarden/common/models/response/notification.response";
|
||||
import { Message, MessageListener } from "@bitwarden/common/platform/messaging";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { SecurityTaskId, UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
|
||||
@@ -39,7 +39,9 @@ describe("Default task service", () => {
|
||||
{ send: mockApiSend } as unknown as ApiService,
|
||||
{ organizations$: mockGetAllOrgs$ } as unknown as OrganizationService,
|
||||
{ authStatuses$: mockAuthStatuses$.asObservable() } as unknown as AuthService,
|
||||
{ notifications$: mockNotifications$.asObservable() } as unknown as NotificationsService,
|
||||
{
|
||||
notifications$: mockNotifications$.asObservable(),
|
||||
} as unknown as ServerNotificationsService,
|
||||
{ allMessages$: mockMessages$.asObservable() } as unknown as MessageListener,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { NotificationType } from "@bitwarden/common/enums";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { MessageListener } from "@bitwarden/common/platform/messaging";
|
||||
import { NotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { SecurityTaskId, UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
@@ -42,7 +42,7 @@ export class DefaultTaskService implements TaskService {
|
||||
private apiService: ApiService,
|
||||
private organizationService: OrganizationService,
|
||||
private authService: AuthService,
|
||||
private notificationService: NotificationsService,
|
||||
private notificationService: ServerNotificationsService,
|
||||
private messageListener: MessageListener,
|
||||
) {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user