mirror of
https://github.com/bitwarden/browser
synced 2026-02-05 19:23:19 +00:00
fix(notification-processing): [PM-19877] System Notification Implementation - Addressed more feedback.
This commit is contained in:
@@ -451,7 +451,7 @@ describe("OverlayNotificationsBackground", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("web requests that trigger notifications", () => {
|
||||
describe("web requests that trigger server notifications", () => {
|
||||
const requestId = "123345";
|
||||
const pageDetails = mock<AutofillPageDetails>({ fields: [mock<AutofillField>()] });
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initialize the overlay notifications background service.
|
||||
* Initialize the overlay server notifications background service.
|
||||
*/
|
||||
async init() {
|
||||
this.setupExtensionListeners();
|
||||
@@ -395,7 +395,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
};
|
||||
|
||||
/**
|
||||
* This method attempts to trigger the add login, change password, or at-risk password notifications
|
||||
* This method attempts to trigger the add login, change password, or at-risk password server notifications
|
||||
* based on the modified login data and the tab details.
|
||||
*
|
||||
* @param requestId - The details of the web response
|
||||
@@ -462,7 +462,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
case NotificationTypes.AtRiskPassword:
|
||||
return !modifyLoginData.newPassword;
|
||||
case NotificationTypes.Unlock:
|
||||
// Unlock notifications are handled separately and do not require form data
|
||||
// Unlock server notifications are handled separately and do not require form data
|
||||
return false;
|
||||
default:
|
||||
this.logService.error(`Unknown notification type: ${notificationType}`);
|
||||
|
||||
@@ -35,7 +35,7 @@ class AutofillInit implements AutofillInitInterface {
|
||||
* @param domElementVisibilityService - Used to check if an element is viewable.
|
||||
* @param autofillOverlayContentService - The autofill overlay content service, potentially undefined.
|
||||
* @param autofillInlineMenuContentService - The inline menu content service, potentially undefined.
|
||||
* @param overlayNotificationsContentService - The overlay notifications content service, potentially undefined.
|
||||
* @param overlayNotificationsContentService - The overlay server notifications content service, potentially undefined.
|
||||
*/
|
||||
constructor(
|
||||
domQueryService: DomQueryService,
|
||||
|
||||
@@ -643,7 +643,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the form field data used for add login and change password notifications.
|
||||
* Returns the form field data used for add login and change password server notifications.
|
||||
*/
|
||||
private getFormFieldData = (): ModifyLoginCipherFormData => {
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
VaultTimeoutSettingsService,
|
||||
VaultTimeoutStringType,
|
||||
} from "@bitwarden/common/key-management/vault-timeout";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
|
||||
const IdleInterval = 60 * 5; // 5 minutes
|
||||
|
||||
|
||||
@@ -119,7 +119,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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
|
||||
import {
|
||||
DefaultServerNotificationsService,
|
||||
@@ -127,9 +127,7 @@ import {
|
||||
UnsupportedWebPushConnectionService,
|
||||
WebPushNotificationsApiService,
|
||||
WorkerWebPushConnectionService,
|
||||
} from "@bitwarden/common/platform/notifications/internal";
|
||||
import { SystemNotificationsService } from "@bitwarden/common/platform/notifications/system-notifications.service";
|
||||
import { UnsupportedSystemNotificationsService } from "@bitwarden/common/platform/notifications/unsupported-system-notifications.service";
|
||||
} from "@bitwarden/common/platform/server-notifications/internal";
|
||||
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";
|
||||
@@ -169,6 +167,8 @@ import { WindowStorageService } from "@bitwarden/common/platform/storage/window-
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
|
||||
import { DefaultSyncService } from "@bitwarden/common/platform/sync/internal";
|
||||
import { SystemNotificationsService } from "@bitwarden/common/platform/system-notifications/";
|
||||
import { UnsupportedSystemNotificationsService } from "@bitwarden/common/platform/system-notifications/unsupported-system-notifications.service";
|
||||
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Observable, Subscription } from "rxjs";
|
||||
|
||||
import { NotificationResponse } from "@bitwarden/common/models/response/notification.response";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
// Eventually if we want to support listening to notifications from browser foreground we
|
||||
// Eventually if we want to support listening to server notifications from browser foreground we
|
||||
// will only ever create a single SignalR connection, likely messaging to the background to reuse its connection.
|
||||
export class ForegroundServerNotificationsService implements ServerNotificationsService {
|
||||
notifications$: Observable<readonly [NotificationResponse, UserId]>;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { map, merge, Observable } from "rxjs";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -9,7 +8,7 @@ import {
|
||||
SystemNotificationCreateInfo,
|
||||
SystemNotificationEvent,
|
||||
SystemNotificationsService,
|
||||
} from "@bitwarden/common/platform/notifications/system-notifications.service";
|
||||
} from "@bitwarden/common/platform/system-notifications/system-notifications.service";
|
||||
|
||||
import { fromChromeEvent } from "../browser/from-chrome-event";
|
||||
|
||||
@@ -36,48 +35,19 @@ export class BrowserSystemNotificationService implements SystemNotificationsServ
|
||||
);
|
||||
}
|
||||
|
||||
async create(createInfo: SystemNotificationCreateInfo): Promise<string | undefined> {
|
||||
try {
|
||||
const notificationId = createInfo.id || uuidv4();
|
||||
|
||||
chrome.notifications.create(notificationId, {
|
||||
iconUrl: "https://avatars.githubusercontent.com/u/15990069?s=200",
|
||||
message: createInfo.body,
|
||||
type: "basic",
|
||||
title: createInfo.title,
|
||||
buttons: createInfo.buttons.map((value) => {
|
||||
return { title: value.title };
|
||||
}),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chrome.notifications.onButtonClicked.addListener(
|
||||
(notificationId: string, buttonIndex: number) => {
|
||||
this.notificationClicked$.subscribe({
|
||||
next: () => ({
|
||||
id: notificationId,
|
||||
buttonIdentifier: buttonIndex,
|
||||
}),
|
||||
});
|
||||
async create(createInfo: SystemNotificationCreateInfo): Promise<string> {
|
||||
return new Promise<string>((resolve) => {
|
||||
chrome.notifications.create(
|
||||
{
|
||||
iconUrl: chrome.runtime.getURL("images/icon128.png"),
|
||||
message: createInfo.body,
|
||||
type: "basic",
|
||||
title: createInfo.title,
|
||||
buttons: createInfo.buttons.map((value) => ({ title: value.title })),
|
||||
},
|
||||
(notificationId) => resolve(notificationId),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chrome.notifications.onClicked.addListener((notificationId: string) => {
|
||||
this.notificationClicked$.subscribe({
|
||||
next: () => ({
|
||||
id: notificationId,
|
||||
buttonIdentifier: ButtonLocation.NotificationButton,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
return notificationId;
|
||||
} catch (e) {
|
||||
this.logService.error(
|
||||
`Failed to create notification on ${this.platformUtilsService.getDevice()} with error: ${e}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async clear(clearInfo: SystemNotificationClearInfo): Promise<undefined> {
|
||||
|
||||
@@ -96,9 +96,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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { SystemNotificationsService } from "@bitwarden/common/platform/notifications/system-notifications.service";
|
||||
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
|
||||
@@ -114,6 +113,7 @@ import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/imp
|
||||
import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service";
|
||||
import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { SystemNotificationsService } from "@bitwarden/common/platform/system-notifications/system-notifications.service";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
||||
@@ -218,7 +218,7 @@ export class AtRiskPasswordsComponent implements OnInit {
|
||||
});
|
||||
}),
|
||||
concatMap((unreadTaskNotifications) => {
|
||||
// TODO: Investigate creating a bulk endpoint to mark notifications as read
|
||||
// TODO: Investigate creating a bulk endpoint to mark server notifications as read
|
||||
return concat(
|
||||
...unreadTaskNotifications.map((n) =>
|
||||
this.endUserNotificationService.markAsRead(n.id, userId),
|
||||
|
||||
@@ -61,7 +61,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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { StateEventRunnerService } from "@bitwarden/common/platform/state";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
@@ -13,7 +13,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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-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";
|
||||
|
||||
@@ -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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-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";
|
||||
|
||||
@@ -84,7 +84,7 @@ import { IpcService } from "@bitwarden/common/platform/ipc";
|
||||
import {
|
||||
UnsupportedWebPushConnectionService,
|
||||
WebPushConnectionService,
|
||||
} from "@bitwarden/common/platform/notifications/internal";
|
||||
} from "@bitwarden/common/platform/server-notifications/internal";
|
||||
import { AppIdService as DefaultAppIdService } from "@bitwarden/common/platform/services/app-id.service";
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
||||
|
||||
@@ -13,7 +13,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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-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";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { SupportStatus } from "@bitwarden/common/platform/misc/support-status";
|
||||
import {
|
||||
WebPushConnector,
|
||||
WorkerWebPushConnectionService,
|
||||
} from "@bitwarden/common/platform/notifications/internal";
|
||||
} from "@bitwarden/common/platform/server-notifications/internal";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export class PermissionsWebPushConnectionService extends WorkerWebPushConnectionService {
|
||||
|
||||
@@ -565,7 +565,7 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
|
||||
this.refreshing = false;
|
||||
|
||||
// Explicitly mark for check to ensure the view is updated
|
||||
// Some sources are not always emitted within the Angular zone (e.g. ciphers updated via WS notifications)
|
||||
// Some sources are not always emitted within the Angular zone (e.g. ciphers updated via WS server notifications)
|
||||
this.changeDetectorRef.markForCheck();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -202,20 +202,20 @@ 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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
|
||||
import {
|
||||
DefaultServerNotificationsService,
|
||||
UnsupportedServerNotificationsService,
|
||||
SignalRConnectionService,
|
||||
UnsupportedWebPushConnectionService,
|
||||
WebPushConnectionService,
|
||||
WebPushNotificationsApiService,
|
||||
} from "@bitwarden/common/platform/notifications/internal";
|
||||
import {
|
||||
DefaultTaskSchedulerService,
|
||||
TaskSchedulerService,
|
||||
} from "@bitwarden/common/platform/scheduling";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
|
||||
import {
|
||||
DefaultServerNotificationsService,
|
||||
NoopServerNotificationsService,
|
||||
SignalRConnectionService,
|
||||
UnsupportedWebPushConnectionService,
|
||||
WebPushConnectionService,
|
||||
WebPushNotificationsApiService,
|
||||
} from "@bitwarden/common/platform/server-notifications/internal";
|
||||
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";
|
||||
@@ -952,7 +952,7 @@ const safeProviders: SafeProvider[] = [
|
||||
safeProvider({
|
||||
provide: ServerNotificationsService,
|
||||
useClass: devFlagEnabled("noopNotifications")
|
||||
? UnsupportedServerNotificationsService
|
||||
? NoopServerNotificationsService
|
||||
: DefaultServerNotificationsService,
|
||||
deps: [
|
||||
LogService,
|
||||
@@ -1115,7 +1115,7 @@ const safeProviders: SafeProvider[] = [
|
||||
// This is a slightly odd dependency tree for a specialized api service
|
||||
// it depends on SyncService so that new data can be retrieved through the sync
|
||||
// rather than updating the OrganizationService directly. Instead OrganizationService
|
||||
// subscribes to sync notifications and will update itself based on that.
|
||||
// subscribes to sync server notifications and will update itself based on that.
|
||||
deps: [ApiServiceAbstraction, SyncService],
|
||||
}),
|
||||
safeProvider({
|
||||
|
||||
@@ -109,9 +109,9 @@ export abstract class AuthRequestServiceAbstraction {
|
||||
): Promise<{ masterKey: MasterKey; masterKeyHash: string }>;
|
||||
|
||||
/**
|
||||
* Handles incoming auth request push notifications.
|
||||
* Handles incoming auth request push server notifications.
|
||||
* @param notification push notification.
|
||||
* @remark We should only be receiving approved push notifications to prevent enumeration.
|
||||
* @remark We should only be receiving approved push server notifications to prevent enumeration.
|
||||
*/
|
||||
abstract sendAuthRequestPushNotification(notification: AuthRequestPushNotification): void;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export class DefaultAuthRequestApiService implements AuthRequestApiServiceAbstra
|
||||
try {
|
||||
// Submit the current device identifier in the header as well as in the POST body.
|
||||
// The value in the header will be used to build the request context and ensure that the resulting
|
||||
// notifications have the current device as a source.
|
||||
// server notifications have the current device as a source.
|
||||
const response = await this.apiService.send(
|
||||
"POST",
|
||||
"/auth-requests/",
|
||||
|
||||
@@ -25,7 +25,7 @@ const SHOW_FAVICONS = new KeyDefinition(DOMAIN_SETTINGS_DISK, "showFavicons", {
|
||||
deserializer: (value: boolean) => value ?? true,
|
||||
});
|
||||
|
||||
// Domain exclusion list for notifications
|
||||
// Domain exclusion list for server notifications
|
||||
const NEVER_DOMAINS = new KeyDefinition(DOMAIN_SETTINGS_DISK, "neverDomains", {
|
||||
deserializer: (value: NeverDomains) => value ?? null,
|
||||
});
|
||||
@@ -64,7 +64,7 @@ export abstract class DomainSettingsService {
|
||||
setShowFavicons: (newValue: boolean) => Promise<void>;
|
||||
|
||||
/**
|
||||
* User-specified URIs for which the client notifications should not appear
|
||||
* User-specified URIs for which the client server notifications should not appear
|
||||
*/
|
||||
neverDomains$: Observable<NeverDomains>;
|
||||
setNeverDomains: (newValue: NeverDomains) => Promise<void>;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// eslint-disable-next-line @bitwarden/platform/no-enums
|
||||
export enum PushTechnology {
|
||||
/**
|
||||
* Indicates that we should use SignalR over web sockets to receive push notifications from the server.
|
||||
* Indicates that we should use SignalR over web sockets to receive push server notifications from the server.
|
||||
*/
|
||||
SignalR = 0,
|
||||
/**
|
||||
* Indicatates that we should use WebPush to receive push notifications from the server.
|
||||
* Indicates that we should use WebPush to receive push server notifications from the server.
|
||||
*/
|
||||
WebPush = 1,
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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) =>
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
1
libs/common/src/platform/system-notifications/index.ts
Normal file
1
libs/common/src/platform/system-notifications/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { SystemNotificationsService } from "./system-notifications.service";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ import { NotificationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { NotificationView } from "../models";
|
||||
|
||||
/**
|
||||
* A service for retrieving and managing notifications for end users.
|
||||
* A service for retrieving and managing server notifications for end users.
|
||||
*/
|
||||
export abstract class EndUserNotificationService {
|
||||
/**
|
||||
* Observable of all notifications for the given user.
|
||||
* Observable of all server notifications for the given user.
|
||||
* @param userId
|
||||
*/
|
||||
abstract notifications$(userId: UserId): Observable<NotificationView[]>;
|
||||
|
||||
/**
|
||||
* Observable of all unread notifications for the given user.
|
||||
* Observable of all unread server notifications for the given user.
|
||||
* @param userId
|
||||
*/
|
||||
abstract unreadNotifications$(userId: UserId): Observable<NotificationView[]>;
|
||||
@@ -35,13 +35,13 @@ export abstract class EndUserNotificationService {
|
||||
abstract markAsDeleted(notificationId: NotificationId, userId: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Clear all notifications from state for the given user.
|
||||
* Clear all server notifications from state for the given user.
|
||||
* @param userId
|
||||
*/
|
||||
abstract clearState(userId: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a subscription to listen for end user push notifications and notification status updates.
|
||||
* Creates a subscription to listen for end user push server notifications and notification status updates.
|
||||
*/
|
||||
abstract listenForEndUserNotifications(): Subscription;
|
||||
}
|
||||
|
||||
@@ -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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { NotificationId, UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
@@ -48,7 +48,7 @@ describe("End User Notification Center Service", () => {
|
||||
});
|
||||
|
||||
describe("notifications$", () => {
|
||||
it("should return notifications from state when not null", async () => {
|
||||
it("should return server notifications from state when not null", async () => {
|
||||
fakeStateProvider.singleUser.mockFor("user-id" as UserId, NOTIFICATIONS, [
|
||||
{
|
||||
id: "notification-id" as NotificationId,
|
||||
@@ -62,7 +62,7 @@ describe("End User Notification Center Service", () => {
|
||||
expect(mockLogService.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return notifications API when state is null", async () => {
|
||||
it("should return server notifications API when state is null", async () => {
|
||||
mockApiService.send.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
@@ -86,7 +86,7 @@ describe("End User Notification Center Service", () => {
|
||||
expect(mockLogService.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should log a warning if there are more notifications available", async () => {
|
||||
it("should log a warning if there are more server notifications available", async () => {
|
||||
mockApiService.send.mockResolvedValue({
|
||||
data: [
|
||||
...new Array(DEFAULT_NOTIFICATION_PAGE_SIZE + 1).fill({ id: "notification-id" }),
|
||||
@@ -120,7 +120,7 @@ describe("End User Notification Center Service", () => {
|
||||
});
|
||||
|
||||
describe("unreadNotifications$", () => {
|
||||
it("should return unread notifications from state when read value is null", async () => {
|
||||
it("should return unread server notifications from state when read value is null", async () => {
|
||||
fakeStateProvider.singleUser.mockFor("user-id" as UserId, NOTIFICATIONS, [
|
||||
{
|
||||
id: "notification-id" as NotificationId,
|
||||
@@ -136,7 +136,7 @@ describe("End User Notification Center Service", () => {
|
||||
});
|
||||
|
||||
describe("getNotifications", () => {
|
||||
it("should call getNotifications returning notifications from API", async () => {
|
||||
it("should call getNotifications returning server notifications from API", async () => {
|
||||
mockApiService.send.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
@@ -156,7 +156,7 @@ describe("End User Notification Center Service", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should update local state when notifications are updated", async () => {
|
||||
it("should update local state when server notifications are updated", async () => {
|
||||
mockApiService.send.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
|
||||
@@ -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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { NotificationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
@@ -19,7 +19,7 @@ import { NotificationView, NotificationViewData, NotificationViewResponse } from
|
||||
import { NOTIFICATIONS } from "../state/end-user-notification.state";
|
||||
|
||||
/**
|
||||
* The default number of notifications to fetch from the API.
|
||||
* The default number of server notifications to fetch from the API.
|
||||
*/
|
||||
export const DEFAULT_NOTIFICATION_PAGE_SIZE = 50;
|
||||
|
||||
@@ -30,7 +30,7 @@ const getLoggedInUserIds = map<Record<UserId, AuthenticationStatus>, UserId[]>((
|
||||
);
|
||||
|
||||
/**
|
||||
* A service for retrieving and managing notifications for end users.
|
||||
* A service for retrieving and managing server notifications for end users.
|
||||
*/
|
||||
export class DefaultEndUserNotificationService implements EndUserNotificationService {
|
||||
constructor(
|
||||
@@ -100,7 +100,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper observable to filter notifications by the notification type and user ids
|
||||
* Helper observable to filter server notifications by the notification type and user ids
|
||||
* Returns EMPTY if no user ids are provided
|
||||
* @param userIds
|
||||
* @private
|
||||
@@ -121,7 +121,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subscription to listen for end user push notifications and notification status updates.
|
||||
* Creates a subscription to listen for end user push server notifications and notification status updates.
|
||||
*/
|
||||
listenForEndUserNotifications(): Subscription {
|
||||
return this.authService.authStatuses$
|
||||
@@ -139,7 +139,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the notifications from the API and updates the local state
|
||||
* Fetches the server notifications from the API and updates the local state
|
||||
* @param userId
|
||||
* @private
|
||||
*/
|
||||
@@ -164,7 +164,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the local state with notifications and returns the updated state
|
||||
* Replaces the local state with server notifications and returns the updated state
|
||||
* @param userId
|
||||
* @param notifications
|
||||
* @private
|
||||
@@ -178,7 +178,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
|
||||
/**
|
||||
* Updates the local state adding the new notification or updates an existing one with the same id
|
||||
* Returns the entire updated notifications state
|
||||
* Returns the entire updated server notifications state
|
||||
* @param userId
|
||||
* @param notification
|
||||
* @private
|
||||
@@ -203,7 +203,7 @@ export class DefaultEndUserNotificationService implements EndUserNotificationSer
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local state for notifications
|
||||
* Returns the local state for server notifications
|
||||
* @param userId
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -45,7 +45,7 @@ export abstract class TaskService {
|
||||
abstract markAsComplete(taskId: SecurityTaskId, userId: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a subscription for pending security task notifications or completed syncs for unlocked users.
|
||||
* Creates a subscription for pending security task server notifications or completed syncs for unlocked users.
|
||||
*/
|
||||
abstract listenForTaskNotifications(): Subscription;
|
||||
}
|
||||
|
||||
@@ -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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { SecurityTaskId, UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
|
||||
@@ -304,7 +304,7 @@ describe("Default task service", () => {
|
||||
});
|
||||
|
||||
describe("listenForTaskNotifications()", () => {
|
||||
it("should not subscribe to notifications when there are no unlocked users", () => {
|
||||
it("should not subscribe to server notifications when there are no unlocked users", () => {
|
||||
mockAuthStatuses$.next({
|
||||
[userId]: AuthenticationStatus.Locked,
|
||||
});
|
||||
@@ -320,7 +320,7 @@ describe("Default task service", () => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
it("should not subscribe to notifications when no users have tasks enabled", () => {
|
||||
it("should not subscribe to server notifications when no users have tasks enabled", () => {
|
||||
mockAuthStatuses$.next({
|
||||
[userId]: AuthenticationStatus.Unlocked,
|
||||
});
|
||||
@@ -336,7 +336,7 @@ describe("Default task service", () => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
it("should subscribe to notifications when there are unlocked users with tasks enabled", () => {
|
||||
it("should subscribe to server notifications when there are unlocked users with tasks enabled", () => {
|
||||
mockAuthStatuses$.next({
|
||||
[userId]: AuthenticationStatus.Unlocked,
|
||||
});
|
||||
@@ -378,7 +378,7 @@ describe("Default task service", () => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
it("should ignore notifications for other users", async () => {
|
||||
it("should ignore server notifications for other users", async () => {
|
||||
mockAuthStatuses$.next({
|
||||
[userId]: AuthenticationStatus.Unlocked,
|
||||
});
|
||||
@@ -403,7 +403,7 @@ describe("Default task service", () => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
it("should ignore other notifications types", async () => {
|
||||
it("should ignore other server notifications types", async () => {
|
||||
mockAuthStatuses$.next({
|
||||
[userId]: AuthenticationStatus.Unlocked,
|
||||
});
|
||||
|
||||
@@ -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 { ServerNotificationsService } from "@bitwarden/common/platform/notifications";
|
||||
import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { SecurityTaskId, UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
@@ -171,7 +171,7 @@ export class DefaultTaskService implements TaskService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subscription for pending security task notifications or completed syncs for unlocked users.
|
||||
* Creates a subscription for pending security task server notifications or completed syncs for unlocked users.
|
||||
*/
|
||||
listenForTaskNotifications(): Subscription {
|
||||
return this.authService.authStatuses$
|
||||
|
||||
@@ -17,7 +17,7 @@ export type ToastOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Presents toast notifications
|
||||
* Presents toast server notifications
|
||||
**/
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class ToastService {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Toast as BaseToastrComponent, ToastPackage, ToastrService } from "ngx-t
|
||||
import { ToastComponent } from "./toast.component";
|
||||
|
||||
/**
|
||||
* Toasts are ephemeral notifications. They most often communicate the result of a user action. Due to their ephemeral nature, long messages and critical alerts should not utilize toasts.
|
||||
* Toasts are ephemeral server notifications. They most often communicate the result of a user action. Due to their ephemeral nature, long messages and critical alerts should not utilize toasts.
|
||||
*/
|
||||
@Component({
|
||||
template: `
|
||||
|
||||
@@ -625,7 +625,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
// Vault can be de-synced since notifications get ignored while locked. Need to check whether sync is required using the sync service.
|
||||
// Vault can be de-synced since server notifications get ignored while locked. Need to check whether sync is required using the sync service.
|
||||
const startSync = new Date().getTime();
|
||||
// TODO: This should probably not be blocking
|
||||
await this.syncService.fullSync(false);
|
||||
|
||||
Reference in New Issue
Block a user