mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 08:43:33 +00:00
[PM-22573] Don't call server on existing subscription (#15154)
* Don't put subscription to our server when it's existing * Only update server when subscription-user associations change --------- Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
Subject,
|
||||
Subscription,
|
||||
switchMap,
|
||||
withLatestFrom,
|
||||
} from "rxjs";
|
||||
|
||||
import { PushTechnology } from "../../../enums/push-technology.enum";
|
||||
@@ -17,6 +18,7 @@ import { UserId } from "../../../types/guid";
|
||||
import { ConfigService } from "../../abstractions/config/config.service";
|
||||
import { SupportStatus } from "../../misc/support-status";
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { KeyDefinition, StateProvider, WEB_PUSH_SUBSCRIPTION } from "../../state";
|
||||
|
||||
import { WebPushNotificationsApiService } from "./web-push-notifications-api.service";
|
||||
import { WebPushConnectionService, WebPushConnector } from "./webpush-connection.service";
|
||||
@@ -48,6 +50,7 @@ export class WorkerWebPushConnectionService implements WebPushConnectionService
|
||||
private readonly configService: ConfigService,
|
||||
private readonly webPushApiService: WebPushNotificationsApiService,
|
||||
private readonly serviceWorkerRegistration: ServiceWorkerRegistration,
|
||||
private readonly stateProvider: StateProvider,
|
||||
) {}
|
||||
|
||||
start(): Subscription {
|
||||
@@ -97,6 +100,7 @@ export class WorkerWebPushConnectionService implements WebPushConnectionService
|
||||
this.serviceWorkerRegistration,
|
||||
this.pushEvent,
|
||||
this.pushChangeEvent,
|
||||
this.stateProvider,
|
||||
),
|
||||
} satisfies SupportStatus<WebPushConnector>;
|
||||
}),
|
||||
@@ -114,20 +118,36 @@ class MyWebPushConnector implements WebPushConnector {
|
||||
private readonly serviceWorkerRegistration: ServiceWorkerRegistration,
|
||||
private readonly pushEvent$: Observable<PushEvent>,
|
||||
private readonly pushChangeEvent$: Observable<PushSubscriptionChangeEvent>,
|
||||
private readonly stateProvider: StateProvider,
|
||||
) {
|
||||
const subscriptionUsersState = this.stateProvider.getGlobal(WEB_PUSH_SUBSCRIPTION_USERS);
|
||||
this.notifications$ = this.getOrCreateSubscription$(this.vapidPublicKey).pipe(
|
||||
concatMap((subscription) => {
|
||||
return defer(() => {
|
||||
if (subscription == null) {
|
||||
throw new Error("Expected a non-null subscription.");
|
||||
}
|
||||
return this.webPushApiService.putSubscription(subscription.toJSON());
|
||||
}).pipe(
|
||||
switchMap(() => this.pushEvent$),
|
||||
map((e) => {
|
||||
return new NotificationResponse(e.data.json().data);
|
||||
}),
|
||||
);
|
||||
withLatestFrom(subscriptionUsersState.state$.pipe(map((x) => x ?? {}))),
|
||||
concatMap(async ([[isExistingSubscription, subscription], subscriptionUsers]) => {
|
||||
if (subscription == null) {
|
||||
throw new Error("Expected a non-null subscription.");
|
||||
}
|
||||
|
||||
// If this is a new subscription, we can clear state and start over
|
||||
if (!isExistingSubscription) {
|
||||
subscriptionUsers = {};
|
||||
}
|
||||
|
||||
// If the user is already subscribed, we don't need to do anything
|
||||
if (subscriptionUsers[subscription.endpoint]?.includes(this.userId)) {
|
||||
return;
|
||||
}
|
||||
subscriptionUsers[subscription.endpoint] ??= [];
|
||||
subscriptionUsers[subscription.endpoint].push(this.userId);
|
||||
// Update the state with the new subscription-user association
|
||||
await subscriptionUsersState.update(() => subscriptionUsers);
|
||||
|
||||
// Inform the server about the new subscription-user association
|
||||
await this.webPushApiService.putSubscription(subscription.toJSON());
|
||||
}),
|
||||
switchMap(() => this.pushEvent$),
|
||||
map((e) => {
|
||||
return new NotificationResponse(e.data.json().data);
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -146,7 +166,7 @@ class MyWebPushConnector implements WebPushConnector {
|
||||
await this.serviceWorkerRegistration.pushManager.getSubscription();
|
||||
|
||||
if (existingSubscription == null) {
|
||||
return await this.pushManagerSubscribe(key);
|
||||
return [false, await this.pushManagerSubscribe(key)] as const;
|
||||
}
|
||||
|
||||
const subscriptionKey = Utils.fromBufferToUrlB64(
|
||||
@@ -159,12 +179,30 @@ class MyWebPushConnector implements WebPushConnector {
|
||||
if (subscriptionKey !== key) {
|
||||
// There is a subscription, but it's not for the current server, unsubscribe and then make a new one
|
||||
await existingSubscription.unsubscribe();
|
||||
return await this.pushManagerSubscribe(key);
|
||||
return [false, await this.pushManagerSubscribe(key)] as const;
|
||||
}
|
||||
|
||||
return existingSubscription;
|
||||
return [true, existingSubscription] as const;
|
||||
}),
|
||||
this.pushChangeEvent$.pipe(map((event) => event.newSubscription)),
|
||||
this.pushChangeEvent$.pipe(map((event) => [false, event.newSubscription] as const)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const WEB_PUSH_SUBSCRIPTION_USERS = new KeyDefinition<Record<string, UserId[]>>(
|
||||
WEB_PUSH_SUBSCRIPTION,
|
||||
"subUsers",
|
||||
{
|
||||
deserializer: (obj) => {
|
||||
if (obj == null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const result: Record<string, UserId[]> = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
result[key] = Array.isArray(value) ? value : [];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user