mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-02 04:48:57 +02:00
Ps/sync only when changed (#4245)
* Only sync if synced value changes
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
* Catch error if no one is listening
This occurs if the background tries to update any visualizers, but none
are open.
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Justin Baur <justindbaur@users.noreply.github.com>
* Allow provided deserializer to handle not found
* Debounce synced items
* Remove object-hash
* Revert "Only sync if synced value changes"
This reverts commit 024fe226d9
.
* Test debounce
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Justin Baur <justindbaur@users.noreply.github.com>
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
parent
8a1230b959
commit
161ff3de28
@ -32,6 +32,7 @@ describe("session syncer", () => {
|
|||||||
stateService = mock<BrowserStateService>();
|
stateService = mock<BrowserStateService>();
|
||||||
stateService.hasInSessionMemory.mockResolvedValue(false);
|
stateService.hasInSessionMemory.mockResolvedValue(false);
|
||||||
sut = new SessionSyncer(behaviorSubject, stateService, metaData);
|
sut = new SessionSyncer(behaviorSubject, stateService, metaData);
|
||||||
|
jest.spyOn(sut as any, "debounceMs", "get").mockReturnValue(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -88,7 +89,7 @@ describe("session syncer", () => {
|
|||||||
|
|
||||||
sut.init();
|
sut.init();
|
||||||
|
|
||||||
expect(sut["ignoreNUpdates"]).toBe(3);
|
expect(sut["ignoreNUpdates"]).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ignore BehaviorSubject's initial value", () => {
|
it("should ignore BehaviorSubject's initial value", () => {
|
||||||
@ -128,28 +129,41 @@ describe("session syncer", () => {
|
|||||||
describe("a value is emitted on the observable", () => {
|
describe("a value is emitted on the observable", () => {
|
||||||
let sendMessageSpy: jest.SpyInstance;
|
let sendMessageSpy: jest.SpyInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
sendMessageSpy = jest.spyOn(BrowserApi, "sendMessage");
|
sendMessageSpy = jest.spyOn(BrowserApi, "sendMessage");
|
||||||
|
|
||||||
sut.init();
|
sut.init();
|
||||||
|
|
||||||
|
// allow initial value to be set
|
||||||
|
await awaitAsync();
|
||||||
behaviorSubject.next("test");
|
behaviorSubject.next("test");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the session memory", async () => {
|
it("should update the session memory", async () => {
|
||||||
// await finishing of fire-and-forget operation
|
// await finishing of fire-and-forget operation
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
await awaitAsync();
|
||||||
expect(stateService.setInSessionMemory).toHaveBeenCalledTimes(1);
|
expect(stateService.setInSessionMemory).toHaveBeenCalledTimes(1);
|
||||||
expect(stateService.setInSessionMemory).toHaveBeenCalledWith(sessionKey, "test");
|
expect(stateService.setInSessionMemory).toHaveBeenCalledWith(sessionKey, "test");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update sessionSyncers in other contexts", async () => {
|
it("should update sessionSyncers in other contexts", async () => {
|
||||||
// await finishing of fire-and-forget operation
|
// await finishing of fire-and-forget operation
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
await awaitAsync();
|
||||||
|
|
||||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(sendMessageSpy).toHaveBeenCalledWith(`${sessionKey}_update`, { id: sut.id });
|
expect(sendMessageSpy).toHaveBeenCalledWith(`${sessionKey}_update`, { id: sut.id });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should debounce subject updates", async () => {
|
||||||
|
behaviorSubject.next("test2");
|
||||||
|
behaviorSubject.next("test3");
|
||||||
|
|
||||||
|
// await finishing of fire-and-forget operation
|
||||||
|
await awaitAsync();
|
||||||
|
|
||||||
|
expect(stateService.setInSessionMemory).toHaveBeenCalledTimes(1);
|
||||||
|
expect(stateService.setInSessionMemory).toHaveBeenCalledWith(sessionKey, "test3");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("A message is received", () => {
|
describe("A message is received", () => {
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { BehaviorSubject, concatMap, ReplaySubject, Subject, Subscription } from "rxjs";
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
concatMap,
|
||||||
|
ReplaySubject,
|
||||||
|
Subject,
|
||||||
|
Subscription,
|
||||||
|
debounceTime,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
import { Utils } from "@bitwarden/common/misc/utils";
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
|
|
||||||
@ -13,6 +20,9 @@ export class SessionSyncer {
|
|||||||
|
|
||||||
// ignore initial values
|
// ignore initial values
|
||||||
private ignoreNUpdates = 0;
|
private ignoreNUpdates = 0;
|
||||||
|
private get debounceMs() {
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private subject: Subject<any>,
|
private subject: Subject<any>,
|
||||||
@ -30,10 +40,8 @@ export class SessionSyncer {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
switch (this.subject.constructor) {
|
switch (this.subject.constructor) {
|
||||||
case ReplaySubject:
|
// ignore all updates currently in the buffer
|
||||||
// ignore all updates currently in the buffer
|
case ReplaySubject: // N = 1 due to debounce
|
||||||
this.ignoreNUpdates = (this.subject as any)._buffer.length;
|
|
||||||
break;
|
|
||||||
case BehaviorSubject:
|
case BehaviorSubject:
|
||||||
this.ignoreNUpdates = 1;
|
this.ignoreNUpdates = 1;
|
||||||
break;
|
break;
|
||||||
@ -58,6 +66,7 @@ export class SessionSyncer {
|
|||||||
// contexts. If so, this is handled by destruction of the context.
|
// contexts. If so, this is handled by destruction of the context.
|
||||||
this.subscription = this.subject
|
this.subscription = this.subject
|
||||||
.pipe(
|
.pipe(
|
||||||
|
debounceTime(this.debounceMs),
|
||||||
concatMap(async (next) => {
|
concatMap(async (next) => {
|
||||||
if (this.ignoreNUpdates > 0) {
|
if (this.ignoreNUpdates > 0) {
|
||||||
this.ignoreNUpdates -= 1;
|
this.ignoreNUpdates -= 1;
|
||||||
@ -92,8 +101,15 @@ export class SessionSyncer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async updateSession(value: any) {
|
private async updateSession(value: any) {
|
||||||
await this.stateService.setInSessionMemory(this.metaData.sessionKey, value);
|
try {
|
||||||
await BrowserApi.sendMessage(this.updateMessageCommand, { id: this.id });
|
await this.stateService.setInSessionMemory(this.metaData.sessionKey, value);
|
||||||
|
await BrowserApi.sendMessage(this.updateMessageCommand, { id: this.id });
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message === "Could not establish connection. Receiving end does not exist.") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get updateMessageCommand() {
|
private get updateMessageCommand() {
|
||||||
|
Loading…
Reference in New Issue
Block a user