1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-05 18:13:26 +00:00

[PM-18903] Desktop sync issues (#13681)

* [PM-18707] Use different BroadcasterSubscriptionId in base view component to avoid collision with desktop view component

* [PM-18707] Use userId instead of payloadUserId for cipher notification syncs

* [PM-19032] Live Sync on Desktop (#13851)

* migrate the vault-items to an observables rather than async/promises

- this helps keep data in sync with the service state and avoids race conditions

* migrate the view component to an observables rather than async/promises

- this helps keep data in sync with the service state and avoids race conditions

* decrypt saved cipher from server

* bump timeout for upserting ciphers

* mark `go` as async in desktop vault

- previously it was a floating promise

* Revert "mark `go` as async in desktop vault"

This reverts commit fd28f40b18.

* Revert "bump timeout for upserting ciphers"

This reverts commit e963acc377.

* move vault utilities to `common` rather than `lib` to avoid circular dependencies

* use `perUserCache$` for `cipherViews$` to avoid new subscriptions from being created

* use userId from observable rather than locally set to be the most up to date

* [PM-18707] Add clearBuffer$ input to perUserCache$ helper so that  the internal share replay buffers can be cleared

* [PM-18707] Rework forceCipherViews$ to clearBuffer$ refactor

- Add dependency for cipherDecryptionKeys$ for the cipherViews so that decryption is never attempted without keys

* [PM-18707] Add overload to perUserCache to satisfy type checker

* [PM-18707] Fix overloads

* [PM-18707] Add check for empty failed to decrypt ciphers

* [PM-18707] Mark vault component for check after observable emits.

The cipherViews$ observable now persists between subscriptions, meaning that updates via the sync push notifications can occur outside the AngularZone causing delays in updating the view.

---------

Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>
Co-authored-by: Nick Krantz <nick@livefront.com>
This commit is contained in:
Shane Melton
2025-04-15 12:17:41 -07:00
committed by GitHub
parent 4cddc40828
commit 8258ea39b0
10 changed files with 239 additions and 168 deletions

View File

@@ -1,20 +1,38 @@
import { filter, Observable, OperatorFunction, shareReplay } from "rxjs";
import { EMPTY, filter, map, merge, Observable, OperatorFunction, shareReplay } from "rxjs";
import { UserId } from "@bitwarden/common/types/guid";
/**
* Builds an observable once per userId and caches it for future requests.
* The built observables are shared among subscribers with a replay buffer size of 1.
*
* Optionally, a clearBuffer$ observable can be provided to clear the replay buffer for a specific or all userIds.
* @param create - A function that creates an observable for a given userId.
* @param clearBuffer$ - An observable that, when emitted, clears the buffer for the emitted userId. When null is emitted, all caches are cleared.
*/
export function perUserCache$<TValue>(
create: (userId: UserId) => Observable<TValue>,
): (userId: UserId) => Observable<TValue> {
const cache = new Map<UserId, Observable<TValue>>();
clearBuffer$: Observable<UserId | null>,
): (userId: UserId) => Observable<TValue | null>;
export function perUserCache$<TValue>(
create: (userId: UserId) => Observable<TValue>,
): (userId: UserId) => Observable<TValue>;
export function perUserCache$<TValue>(
create: (userId: UserId) => Observable<TValue>,
clearBuffer$: Observable<UserId | null> | undefined = undefined,
): (userId: UserId) => Observable<TValue | null> {
const cache = new Map<UserId, Observable<TValue | null>>();
return (userId: UserId) => {
let observable = cache.get(userId);
if (!observable) {
observable = create(userId).pipe(shareReplay({ bufferSize: 1, refCount: false }));
clearBuffer$ ??= EMPTY;
observable = merge(
create(userId),
clearBuffer$.pipe(
filter((clearId) => clearId === userId || clearId === null),
map(() => null),
),
).pipe(shareReplay({ bufferSize: 1, refCount: false }));
cache.set(userId, observable);
}
return observable;