1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-06 18:43:25 +00:00

Merge branch 'main' into autofill/pm-8518-autofill-scripts-do-not-inject-into-sub-frames-on-install

This commit is contained in:
Cesar Gonzalez
2024-06-05 15:14:50 -05:00
committed by GitHub
73 changed files with 980 additions and 846 deletions

View File

@@ -77,7 +77,9 @@ type OverlayBackgroundExtensionMessageHandlers = {
updateFocusedFieldData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
collectPageDetailsResponse: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
unlockCompleted: ({ message }: BackgroundMessageParam) => void;
addedCipher: () => void;
addEditCipherSubmitted: () => void;
editedCipher: () => void;
deletedCipher: () => void;
};

View File

@@ -1000,29 +1000,23 @@ describe("OverlayBackground", () => {
});
});
describe("addEditCipherSubmitted message handler", () => {
it("updates the overlay ciphers", () => {
const message = {
command: "addEditCipherSubmitted",
};
jest.spyOn(overlayBackground as any, "updateOverlayCiphers").mockImplementation();
describe("extension messages that trigger an update of the inline menu ciphers", () => {
const extensionMessages = [
"addedCipher",
"addEditCipherSubmitted",
"editedCipher",
"deletedCipher",
];
sendMockExtensionMessage(message);
expect(overlayBackground["updateOverlayCiphers"]).toHaveBeenCalled();
beforeEach(() => {
jest.spyOn(overlayBackground, "updateOverlayCiphers").mockImplementation();
});
});
describe("deletedCipher message handler", () => {
it("updates the overlay ciphers", () => {
const message = {
command: "deletedCipher",
};
jest.spyOn(overlayBackground as any, "updateOverlayCiphers").mockImplementation();
sendMockExtensionMessage(message);
expect(overlayBackground["updateOverlayCiphers"]).toHaveBeenCalled();
extensionMessages.forEach((message) => {
it(`triggers an update of the overlay ciphers when the ${message} message is received`, () => {
sendMockExtensionMessage({ command: message });
expect(overlayBackground.updateOverlayCiphers).toHaveBeenCalled();
});
});
});
});

View File

@@ -72,7 +72,9 @@ class OverlayBackground implements OverlayBackgroundInterface {
updateFocusedFieldData: ({ message, sender }) => this.setFocusedFieldData(message, sender),
collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender),
unlockCompleted: ({ message }) => this.unlockCompleted(message),
addedCipher: () => this.updateOverlayCiphers(),
addEditCipherSubmitted: () => this.updateOverlayCiphers(),
editedCipher: () => this.updateOverlayCiphers(),
deletedCipher: () => this.updateOverlayCiphers(),
};
private readonly overlayButtonPortMessageHandlers: OverlayButtonPortMessageHandlers = {

View File

@@ -136,6 +136,9 @@ import { DefaultStateProvider } from "@bitwarden/common/platform/state/implement
import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/inline-derived-state";
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
/* eslint-enable import/no-restricted-paths */
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 { 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";
@@ -166,8 +169,6 @@ import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/co
import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync-notifier.service.abstraction";
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -176,8 +177,6 @@ import { CollectionService } from "@bitwarden/common/vault/services/collection.s
import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service";
import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service";
import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service";
import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-notifier.service";
import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service";
import {
@@ -268,7 +267,7 @@ export default class MainBackground {
collectionService: CollectionServiceAbstraction;
vaultTimeoutService: VaultTimeoutService;
vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction;
syncService: SyncServiceAbstraction;
syncService: SyncService;
passwordGenerationService: PasswordGenerationServiceAbstraction;
passwordStrengthService: PasswordStrengthServiceAbstraction;
totpService: TotpServiceAbstraction;
@@ -306,7 +305,6 @@ export default class MainBackground {
policyApiService: PolicyApiServiceAbstraction;
sendApiService: SendApiServiceAbstraction;
userVerificationApiService: UserVerificationApiServiceAbstraction;
syncNotifierService: SyncNotifierServiceAbstraction;
fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction;
fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction;
fido2ClientService: Fido2ClientServiceAbstraction;
@@ -638,7 +636,6 @@ export default class MainBackground {
this.i18nService,
this.stateProvider,
);
this.syncNotifierService = new SyncNotifierService();
this.autofillSettingsService = new AutofillSettingsService(
this.stateProvider,
@@ -827,7 +824,7 @@ export default class MainBackground {
messageListener,
);
} else {
this.syncService = new SyncService(
this.syncService = new DefaultSyncService(
this.masterPasswordService,
this.accountService,
this.apiService,

View File

@@ -1,11 +0,0 @@
<div class="tw-flex tw-justify-between tw-items-end tw-gap-1 tw-px-1 tw-pb-1">
<div class="tw-flex tw-items-center tw-gap-1">
<h2 bitTypography="h6" noMargin class="tw-mb-0 tw-text-headers">
{{ title }}
</h2>
<ng-content select="[slot=title-suffix]"></ng-content>
</div>
<div class="tw-text-muted has-[button]:-tw-mb-1">
<ng-content select="[slot=end]"></ng-content>
</div>
</div>

View File

@@ -1,13 +0,0 @@
import { Component, Input } from "@angular/core";
import { TypographyModule } from "@bitwarden/components";
@Component({
standalone: true,
selector: "popup-section-header",
templateUrl: "./popup-section-header.component.html",
imports: [TypographyModule],
})
export class PopupSectionHeaderComponent {
@Input() title: string;
}

View File

@@ -1,104 +0,0 @@
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";
import {
CardComponent,
IconButtonModule,
SectionComponent,
TypographyModule,
} from "@bitwarden/components";
import { PopupSectionHeaderComponent } from "./popup-section-header.component";
export default {
title: "Browser/Popup Section Header",
component: PopupSectionHeaderComponent,
args: {
title: "Title",
},
decorators: [
moduleMetadata({
imports: [SectionComponent, CardComponent, TypographyModule, IconButtonModule],
}),
],
} as Meta<PopupSectionHeaderComponent>;
type Story = StoryObj<PopupSectionHeaderComponent>;
export const OnlyTitle: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title"></popup-section-header>
`,
}),
args: {
title: "Only Title",
},
};
export const TrailingText: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title">
<span bitTypography="body2" slot="end">13</span>
</popup-section-header>
`,
}),
args: {
title: "Trailing Text",
},
};
export const TailingIcon: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title">
<button bitIconButton="bwi-star" size="small" slot="end"></button>
</popup-section-header>
`,
}),
args: {
title: "Trailing Icon",
},
};
export const TitleSuffix: Story = {
render: (args) => ({
props: args,
template: `
<popup-section-header [title]="title">
<button bitIconButton="bwi-refresh" size="small" slot="title-suffix"></button>
</popup-section-header>
`,
}),
args: {
title: "Title Suffix",
},
};
export const WithSections: Story = {
render: () => ({
template: `
<div class="tw-bg-background-alt tw-p-2">
<bit-section>
<popup-section-header title="Section 1">
<button bitIconButton="bwi-star" size="small" slot="end"></button>
</popup-section-header>
<bit-card>
<h3 bitTypography="h3">Card 1 Content</h3>
</bit-card>
</bit-section>
<bit-section>
<popup-section-header title="Section 2">
<button bitIconButton="bwi-star" size="small" slot="end"></button>
</popup-section-header>
<bit-card>
<h3 bitTypography="h3">Card 2 Content</h3>
</bit-card>
</bit-section>
</div>
`,
}),
};

View File

@@ -46,7 +46,6 @@ import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.comp
import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../platform/popup/layout/popup-page.component";
import { PopupTabNavigationComponent } from "../platform/popup/layout/popup-tab-navigation.component";
import { PopupSectionHeaderComponent } from "../platform/popup/popup-section-header/popup-section-header.component";
import { FilePopoutCalloutComponent } from "../tools/popup/components/file-popout-callout.component";
import { GeneratorComponent } from "../tools/popup/generator/generator.component";
import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component";
@@ -119,7 +118,6 @@ import "../platform/popup/locales";
PopupFooterComponent,
PopupHeaderComponent,
UserVerificationDialogComponent,
PopupSectionHeaderComponent,
CurrentAccountComponent,
],
declarations: [

View File

@@ -80,11 +80,11 @@ import {
} from "@bitwarden/common/platform/state";
// eslint-disable-next-line import/no-restricted-paths -- Used for dependency injection
import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/inline-derived-state";
import { SyncService } from "@bitwarden/common/platform/sync";
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import { DialogService, ToastService } from "@bitwarden/components";

View File

@@ -8,17 +8,19 @@
></app-vault-list-items-container>
<ng-container *ngIf="showEmptyAutofillTip$ | async">
<bit-section>
<popup-section-header [title]="'autofillSuggestions' | i18n">
<bit-section-header>
<h2 bitTypography="h6">
{{ "autofillSuggestions" | i18n }}
</h2>
<button
*ngIf="showRefresh"
bitIconButton="bwi-refresh"
size="small"
slot="title-suffix"
type="button"
[appA11yTitle]="'refresh' | i18n"
(click)="refreshCurrentTab()"
></button>
</popup-section-header>
</bit-section-header>
<span class="tw-text-muted tw-px-1" bitTypography="body2">{{
"autofillSuggestionsTip" | i18n
}}</span>

View File

@@ -3,10 +3,14 @@ import { Component } from "@angular/core";
import { combineLatest, map, Observable } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { IconButtonModule, SectionComponent, TypographyModule } from "@bitwarden/components";
import {
IconButtonModule,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
} from "@bitwarden/components";
import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
import { PopupSectionHeaderComponent } from "../../../../../platform/popup/popup-section-header/popup-section-header.component";
import { VaultPopupItemsService } from "../../../services/vault-popup-items.service";
import { PopupCipherView } from "../../../views/popup-cipher.view";
import { VaultListItemsContainerComponent } from "../vault-list-items-container/vault-list-items-container.component";
@@ -19,7 +23,7 @@ import { VaultListItemsContainerComponent } from "../vault-list-items-container/
TypographyModule,
VaultListItemsContainerComponent,
JslibModule,
PopupSectionHeaderComponent,
SectionHeaderComponent,
IconButtonModule,
],
selector: "app-autofill-vault-list-items",

View File

@@ -1,16 +1,18 @@
<bit-section *ngIf="ciphers?.length > 0">
<popup-section-header [title]="title">
<span bitTypography="body2" slot="end">{{ ciphers.length }}</span>
<bit-section-header>
<h2 bitTypography="h6">
{{ title }}
</h2>
<button
*ngIf="showRefresh"
bitIconButton="bwi-refresh"
type="button"
size="small"
slot="title-suffix"
(click)="onRefresh.emit()"
[appA11yTitle]="'refresh' | i18n"
></button>
</popup-section-header>
<span bitTypography="body2" slot="end">{{ ciphers.length }}</span>
</bit-section-header>
<bit-item-group>
<bit-item *ngFor="let cipher of ciphers">
<a

View File

@@ -10,10 +10,10 @@ import {
IconButtonModule,
ItemModule,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
} from "@bitwarden/components";
import { PopupSectionHeaderComponent } from "../../../../../platform/popup/popup-section-header/popup-section-header.component";
import { PopupCipherView } from "../../../views/popup-cipher.view";
import { ItemCopyActionsComponent } from "../item-copy-action/item-copy-actions.component";
import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options.component";
@@ -28,7 +28,7 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options
SectionComponent,
TypographyModule,
JslibModule,
PopupSectionHeaderComponent,
SectionHeaderComponent,
RouterLink,
ItemCopyActionsComponent,
ItemMoreOptionsComponent,

View File

@@ -1,12 +1,14 @@
import { CommonModule } from "@angular/common";
import { Component, Output, EventEmitter } from "@angular/core";
import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormsModule } from "@angular/forms";
import { Subject, debounceTime } from "rxjs";
import { Subject, Subscription, debounceTime, filter } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SearchModule } from "@bitwarden/components";
import { VaultPopupItemsService } from "../../../services/vault-popup-items.service";
const SearchTextDebounceInterval = 200;
@Component({
@@ -17,19 +19,34 @@ const SearchTextDebounceInterval = 200;
})
export class VaultV2SearchComponent {
searchText: string;
@Output() searchTextChanged = new EventEmitter<string>();
private searchText$ = new Subject<string>();
constructor() {
this.searchText$
.pipe(debounceTime(SearchTextDebounceInterval), takeUntilDestroyed())
.subscribe((data) => {
this.searchTextChanged.emit(data);
});
constructor(private vaultPopupItemsService: VaultPopupItemsService) {
this.subscribeToLatestSearchText();
this.subscribeToApplyFilter();
}
onSearchTextChanged() {
this.searchText$.next(this.searchText);
}
subscribeToLatestSearchText(): Subscription {
return this.vaultPopupItemsService.latestSearchText$
.pipe(
takeUntilDestroyed(),
filter((data) => !!data),
)
.subscribe((text) => {
this.searchText = text;
});
}
subscribeToApplyFilter(): Subscription {
return this.searchText$
.pipe(debounceTime(SearchTextDebounceInterval), takeUntilDestroyed())
.subscribe((data) => {
this.vaultPopupItemsService.applyFilter(data);
});
}
}

View File

@@ -14,8 +14,8 @@ 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 { Utils } from "@bitwarden/common/platform/misc/utils";
import { SyncService } from "@bitwarden/common/platform/sync";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";

View File

@@ -9,8 +9,8 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";

View File

@@ -22,18 +22,15 @@
</div>
<ng-container *ngIf="!(showEmptyState$ | async)">
<div class="tw-fixed">
<app-vault-v2-search (searchTextChanged)="handleSearchTextChange($event)">
</app-vault-v2-search>
<app-vault-v2-search> </app-vault-v2-search>
<app-vault-list-filters></app-vault-list-filters>
</div>
<app-vault-list-filters></app-vault-list-filters>
<div
*ngIf="(showNoResultsState$ | async) && !(showDeactivatedOrg$ | async)"
class="tw-flex tw-flex-col tw-h-full tw-justify-center"
class="tw-flex tw-flex-col tw-justify-center tw-h-auto tw-pt-12"
>
<bit-no-items>
<bit-no-items [icon]="noResultsIcon">
<ng-container slot="title">{{ "noItemsMatchSearch" | i18n }}</ng-container>
<ng-container slot="description">{{ "clearFiltersOrTryAnother" | i18n }}</ng-container>
</bit-no-items>
@@ -41,7 +38,7 @@
<div
*ngIf="showDeactivatedOrg$ | async"
class="tw-flex tw-flex-col tw-h-full tw-justify-center"
class="tw-flex tw-flex-col tw-justify-center tw-h-auto tw-pt-12"
>
<bit-no-items [icon]="deactivatedIcon">
<ng-container slot="title">{{ "organizationIsDeactivated" | i18n }}</ng-container>

View File

@@ -44,6 +44,7 @@ export class VaultV2Component implements OnInit, OnDestroy {
protected vaultIcon = Icons.Vault;
protected deactivatedIcon = Icons.DeactivatedOrg;
protected noResultsIcon = Icons.NoResults;
constructor(
private vaultPopupItemsService: VaultPopupItemsService,
@@ -54,10 +55,6 @@ export class VaultV2Component implements OnInit, OnDestroy {
ngOnDestroy(): void {}
handleSearchTextChange(searchText: string) {
this.vaultPopupItemsService.applyFilter(searchText);
}
addCipher() {
// TODO: Add currently filtered organization to query params if available
void this.router.navigate(["/add-cipher"], {});

View File

@@ -6,6 +6,7 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { ProductType } from "@bitwarden/common/enums";
import { ObservableTracker } from "@bitwarden/common/spec";
import { CipherId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
@@ -50,7 +51,8 @@ describe("VaultPopupItemsService", () => {
cipherList[3].favorite = true;
cipherServiceMock.getAllDecrypted.mockResolvedValue(cipherList);
cipherServiceMock.ciphers$ = new BehaviorSubject(null).asObservable();
cipherServiceMock.ciphers$ = new BehaviorSubject(null);
cipherServiceMock.localData$ = new BehaviorSubject(null);
searchService.searchCiphers.mockImplementation(async (_, __, ciphers) => ciphers);
cipherServiceMock.filterCiphersForUrl.mockImplementation(async (ciphers) =>
ciphers.filter((c) => ["0", "1"].includes(c.id)),
@@ -123,6 +125,34 @@ describe("VaultPopupItemsService", () => {
});
});
it("should update cipher list when cipherService.ciphers$ emits", async () => {
const tracker = new ObservableTracker(service.autoFillCiphers$);
await tracker.expectEmission();
(cipherServiceMock.ciphers$ as BehaviorSubject<any>).next(null);
await tracker.expectEmission();
// Should only emit twice
expect(tracker.emissions.length).toBe(2);
await expect(tracker.pauseUntilReceived(3)).rejects.toThrow("Timeout exceeded");
});
it("should update cipher list when cipherService.localData$ emits", async () => {
const tracker = new ObservableTracker(service.autoFillCiphers$);
await tracker.expectEmission();
(cipherServiceMock.localData$ as BehaviorSubject<any>).next(null);
await tracker.expectEmission();
// Should only emit twice
expect(tracker.emissions.length).toBe(2);
await expect(tracker.pauseUntilReceived(3)).rejects.toThrow("Timeout exceeded");
});
describe("autoFillCiphers$", () => {
it("should return empty array if there is no current tab", (done) => {
jest.spyOn(BrowserApi, "getTabFromCurrentWindow").mockResolvedValue(null);

View File

@@ -5,6 +5,7 @@ import {
distinctUntilKeyChanged,
from,
map,
merge,
Observable,
of,
shareReplay,
@@ -38,7 +39,8 @@ import { MY_VAULT_ID, VaultPopupListFiltersService } from "./vault-popup-list-fi
})
export class VaultPopupItemsService {
private _refreshCurrentTab$ = new Subject<void>();
private searchText$ = new BehaviorSubject<string>("");
private _searchText$ = new BehaviorSubject<string>("");
latestSearchText$: Observable<string> = this._searchText$.asObservable();
/**
* Observable that contains the list of other cipher types that should be shown
@@ -77,10 +79,12 @@ export class VaultPopupItemsService {
* Observable that contains the list of all decrypted ciphers.
* @private
*/
private _cipherList$: Observable<PopupCipherView[]> = this.cipherService.ciphers$.pipe(
private _cipherList$: Observable<PopupCipherView[]> = merge(
this.cipherService.ciphers$,
this.cipherService.localData$,
).pipe(
runInsideAngular(inject(NgZone)), // Workaround to ensure cipher$ state provider emissions are run inside Angular
switchMap(() => Utils.asyncToObservable(() => this.cipherService.getAllDecrypted())),
map((ciphers) => Object.values(ciphers)),
switchMap((ciphers) =>
combineLatest([
this.organizationService.organizations$,
@@ -105,7 +109,7 @@ export class VaultPopupItemsService {
private _filteredCipherList$: Observable<PopupCipherView[]> = combineLatest([
this._cipherList$,
this.searchText$,
this._searchText$,
this.vaultPopupListFiltersService.filterFunction$,
]).pipe(
map(([ciphers, searchText, filterFunction]): [CipherView[], string] => [
@@ -179,7 +183,7 @@ export class VaultPopupItemsService {
* Observable that indicates whether a filter is currently applied to the ciphers.
*/
hasFilterApplied$ = combineLatest([
this.searchText$,
this._searchText$,
this.vaultPopupListFiltersService.filters$,
]).pipe(
switchMap(([searchText, filters]) => {
@@ -242,7 +246,7 @@ export class VaultPopupItemsService {
}
applyFilter(newSearchText: string) {
this.searchText$.next(newSearchText);
this._searchText$.next(newSearchText);
}
/**

View File

@@ -2,7 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { SyncService } from "@bitwarden/common/platform/sync";
@Component({
selector: "app-sync",