1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-04 10:43:47 +00:00

created VaultStateService

This commit is contained in:
Leslie Xiong
2025-12-02 11:52:33 -05:00
parent 6bd297dee3
commit bbf20eaf5a
4 changed files with 149 additions and 4 deletions

View File

@@ -0,0 +1,108 @@
import { Injectable } from "@angular/core";
import { Subject } from "rxjs";
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SearchBarService } from "../app/layout/search/search-bar.service";
/**
* Service to coordinate vault state, including filter state and folder actions,
* between the navigation component and the vault component.
*/
@Injectable({ providedIn: "root" })
export class VaultStateService {
private filterChangeSubject = new Subject<VaultFilter>();
private addFolderSubject = new Subject<void>();
private editFolderSubject = new Subject<string>();
/**
* The currently active vault filter.
*/
activeFilter: VaultFilter = new VaultFilter();
/**
* Observable stream of vault filter changes.
* Subscribe to this to react to filter changes from the navigation.
*/
readonly filterChange$ = this.filterChangeSubject.asObservable();
/**
* Observable stream of add folder requests.
* Subscribe to this to handle folder creation.
*/
readonly addFolder$ = this.addFolderSubject.asObservable();
/**
* Observable stream of edit folder requests.
* Subscribe to this to handle folder editing.
* Emits the folder ID to edit.
*/
readonly editFolder$ = this.editFolderSubject.asObservable();
constructor(
private i18nService: I18nService,
private searchBarService: SearchBarService,
) {}
/**
* Apply a new vault filter.
* This updates the search bar placeholder and notifies all subscribers.
*/
applyFilter(filter: VaultFilter): void {
// Store the active filter
this.activeFilter = filter;
// Update search bar placeholder text based on the filter
this.searchBarService.setPlaceholderText(
this.i18nService.t(this.calculateSearchBarLocalizationString(filter)),
);
// Emit the filter change to subscribers
this.filterChangeSubject.next(filter);
}
/**
* Request to add a new folder.
* This will notify subscribers to show the folder creation dialog.
*/
requestAddFolder(): void {
this.addFolderSubject.next();
}
/**
* Request to edit an existing folder.
* This will notify subscribers to show the folder edit dialog.
*/
requestEditFolder(folderId: string): void {
this.editFolderSubject.next(folderId);
}
/**
* Calculate the appropriate search bar localization string based on the active filter.
*/
private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string {
if (vaultFilter.status === "favorites") {
return "searchFavorites";
}
if (vaultFilter.status === "trash") {
return "searchTrash";
}
if (vaultFilter.cipherType != null) {
return "searchType";
}
if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId !== "none") {
return "searchFolder";
}
if (vaultFilter.selectedCollectionId != null) {
return "searchCollection";
}
if (vaultFilter.selectedOrganizationId != null) {
return "searchOrganization";
}
if (vaultFilter.myVaultOnly) {
return "searchMyVault";
}
return "searchVault";
}
}

View File

@@ -1 +1,9 @@
<bit-nav-group icon="bwi-vault" [text]="'vault' | i18n" route="new-vault"></bit-nav-group>
<bit-nav-group icon="bwi-vault" [text]="'vault' | i18n" route="new-vault">
<app-vault-filter
class="vault-filters"
[activeFilter]="vaultStateService.activeFilter"
(onFilterChange)="vaultStateService.applyFilter($event)"
(onAddFolder)="vaultStateService.requestAddFolder()"
(onEditFolder)="vaultStateService.requestEditFolder($event.id)"
></app-vault-filter>
</bit-nav-group>

View File

@@ -1,12 +1,15 @@
import { ChangeDetectionStrategy, Component } from "@angular/core";
import { NavigationModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
import { VaultStateService } from "../../../../services/vault-state.service";
import { VaultFilterModule } from "../vault-filter/vault-filter.module";
@Component({
selector: "app-vault-nav",
imports: [I18nPipe, NavigationModule],
imports: [I18nPipe, NavigationModule, VaultFilterModule],
templateUrl: "./vault-nav.component.html",
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VaultNavComponent {}
export class VaultNavComponent {
constructor(protected vaultStateService: VaultStateService) {}
}

View File

@@ -83,6 +83,7 @@ import {
import { SearchBarService } from "../../../app/layout/search/search-bar.service";
import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service";
import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service";
import { VaultStateService } from "../../../services/vault-state.service";
import { invokeMenu, RendererMenuItem } from "../../../utils";
import { AssignCollectionsDesktopComponent } from "../vault/assign-collections";
import { ItemFooterComponent } from "../vault/item-footer.component";
@@ -225,6 +226,7 @@ export class VaultComponent<C extends CipherViewLike>
private cipherArchiveService: CipherArchiveService,
private policyService: PolicyService,
private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService,
private vaultStateService: VaultStateService,
) {}
async ngOnInit() {
@@ -240,6 +242,30 @@ export class VaultComponent<C extends CipherViewLike>
this.userHasPremiumAccess = canAccessPremium;
});
// Subscribe to filter changes from VaultNavComponent
this.vaultStateService.filterChange$
.pipe(
switchMap((vaultFilter: VaultFilter) => this.applyVaultFilter(vaultFilter)),
takeUntil(this.componentIsDestroyed$),
)
.subscribe();
// Subscribe to add folder requests from VaultNavComponent
this.vaultStateService.addFolder$
.pipe(
switchMap(() => this.addFolder()),
takeUntil(this.componentIsDestroyed$),
)
.subscribe();
// Subscribe to edit folder requests from VaultNavComponent
this.vaultStateService.editFolder$
.pipe(
switchMap((folderId: string) => this.editFolder(folderId)),
takeUntil(this.componentIsDestroyed$),
)
.subscribe();
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone
.run(async () => {