mirror of
https://github.com/bitwarden/browser
synced 2026-02-01 09:13:54 +00:00
reduce emissions to vault list
This commit is contained in:
@@ -109,7 +109,10 @@ export class VaultPopupItemsService {
|
||||
map((a) => a?.id),
|
||||
filter((userId): userId is UserId => userId != null),
|
||||
switchMap((userId) =>
|
||||
merge(this.cipherService.ciphers$(userId), this.cipherService.localData$(userId)).pipe(
|
||||
merge(
|
||||
this.cipherService.ciphers$(userId),
|
||||
this.cipherService.ciphersWithLocalData$(userId),
|
||||
).pipe(
|
||||
runInsideAngular(this.ngZone),
|
||||
tap(() => this._ciphersLoading$.next()),
|
||||
waitUntilSync(this.syncService),
|
||||
@@ -163,6 +166,7 @@ export class VaultPopupItemsService {
|
||||
}),
|
||||
),
|
||||
),
|
||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,16 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
abstract cipherListViews$(userId: UserId): Observable<CipherListView[] | CipherView[]>;
|
||||
abstract ciphers$(userId: UserId): Observable<Record<CipherId, CipherData>>;
|
||||
abstract localData$(userId: UserId): Observable<Record<CipherId, LocalData>>;
|
||||
/**
|
||||
* Emits decrypted (or list-view) ciphers merged with their LocalData.
|
||||
*
|
||||
* The emitted items must be treated as {@link CipherViewLike},
|
||||
* since the underlying implementation may emit either `CipherView`
|
||||
* or `CipherListView` depending on feature flags.
|
||||
*
|
||||
* Never emits `null`; always emits an array (empty or populated).
|
||||
*/
|
||||
abstract ciphersWithLocalData$(userId: UserId): Observable<CipherViewLike[]>;
|
||||
/**
|
||||
* An observable monitoring the add/edit cipher info saved to memory.
|
||||
*/
|
||||
|
||||
@@ -168,6 +168,57 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
);
|
||||
});
|
||||
|
||||
private cipherListWithoutLocalData$ = perUserCache$((userId: UserId) => {
|
||||
return this.configService.getFeatureFlag$(FeatureFlag.PM22134SdkCipherListView).pipe(
|
||||
switchMap((useSdk) => {
|
||||
if (!useSdk) {
|
||||
return this.cipherViews$(userId).pipe(
|
||||
// filter out "null" decrypt-in-progress if it ever appears
|
||||
filter((ciphers): ciphers is CipherView[] => ciphers != null),
|
||||
);
|
||||
}
|
||||
|
||||
return combineLatest([
|
||||
this.encryptedCiphersState(userId).state$,
|
||||
this.keyService.cipherDecryptionKeys$(userId, true),
|
||||
]).pipe(
|
||||
filter(([cipherDataState, keys]) => cipherDataState != null && keys != null),
|
||||
map(([cipherDataState]) => Object.values(cipherDataState)),
|
||||
switchMap(async (cipherDataArray) => {
|
||||
const ciphers = cipherDataArray.map((c) => new Cipher(c));
|
||||
const [decrypted, failures] = await this.decryptCiphersWithSdk(ciphers, userId, false);
|
||||
await this.setFailedDecryptedCiphers(failures, userId);
|
||||
return decrypted;
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
ciphersWithLocalData$(userId: UserId): Observable<CipherViewLike[]> {
|
||||
return combineLatest([this.cipherListViews$(userId), this.localData$(userId)]).pipe(
|
||||
map(([ciphers, localData]) => {
|
||||
if (!ciphers) {
|
||||
return [] as CipherViewLike[];
|
||||
}
|
||||
|
||||
return ciphers.map((cipher) => {
|
||||
const cipherId = uuidAsString(cipher.id) as CipherId;
|
||||
const local = localData?.[cipherId];
|
||||
|
||||
if (!local) {
|
||||
return cipher as CipherViewLike;
|
||||
}
|
||||
|
||||
return {
|
||||
...(cipher as CipherViewLike),
|
||||
localData: local,
|
||||
} as CipherViewLike;
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observable that emits an array of decrypted ciphers for the active user.
|
||||
* This observable will not emit until the encrypted ciphers have either been loaded from state or after sync.
|
||||
@@ -178,10 +229,9 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
cipherViews$ = perUserCache$((userId: UserId): Observable<CipherView[] | null> => {
|
||||
return combineLatest([
|
||||
this.encryptedCiphersState(userId).state$,
|
||||
this.localData$(userId),
|
||||
this.keyService.cipherDecryptionKeys$(userId),
|
||||
]).pipe(
|
||||
filter(([ciphers, _, keys]) => ciphers != null && keys != null), // Skip if ciphers haven't been loaded yor synced yet
|
||||
filter(([ciphers, keys]) => ciphers != null && keys != null),
|
||||
switchMap(() => this.getAllDecrypted(userId)),
|
||||
tap(() => {
|
||||
this.messageSender.send("updateOverlayCiphers");
|
||||
@@ -796,27 +846,18 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
const cipherId = id as CipherId;
|
||||
if (ciphersLocalData[cipherId]) {
|
||||
ciphersLocalData[cipherId].lastUsedDate = new Date().getTime();
|
||||
} else {
|
||||
ciphersLocalData[cipherId] = { lastUsedDate: new Date().getTime() };
|
||||
}
|
||||
const now = new Date().getTime();
|
||||
|
||||
await this.localDataState(userId).update(() => ciphersLocalData);
|
||||
|
||||
const decryptedCipherCache = await this.getDecryptedCiphers(userId);
|
||||
if (!decryptedCipherCache) {
|
||||
if (ciphersLocalData[cipherId]?.lastUsedDate === now) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < decryptedCipherCache.length; i++) {
|
||||
const cached = decryptedCipherCache[i];
|
||||
if (cached.id === id) {
|
||||
cached.localData = ciphersLocalData[id as CipherId];
|
||||
break;
|
||||
}
|
||||
}
|
||||
await this.setDecryptedCiphers(decryptedCipherCache, userId);
|
||||
ciphersLocalData[cipherId] = {
|
||||
...(ciphersLocalData[cipherId] ?? {}),
|
||||
lastUsedDate: now,
|
||||
};
|
||||
|
||||
await this.localDataState(userId).update(() => ciphersLocalData);
|
||||
}
|
||||
|
||||
async updateLastLaunchedDate(id: string, userId: UserId): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user