mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-23 21:31:29 +01:00
[PM-6992] Refactor VaultComponent (#10126)
* Added function to return cipherview observable and trigger the decryption process if the cipherviews$ observable returns empty * Updated the vault component to use getCipherViews$ observable function * converted vault banner to standalone component * converted vault header to standalone component * fixed unawaited promises converted component to standalone component * cleaned up vault module * fixed imports * refactored getCipherView$ observable * refactored onVaultItemsEvent to switch case * Refactored to use toast service instead of platform utils service for toast * Added function to return cipherview observable and trigger the decryption process if the cipherviews$ observable returns empty * Updated the vault component to use getCipherViews$ observable function * converted vault banner to standalone component * converted vault header to standalone component * fixed unawaited promises converted component to standalone component * cleaned up vault module * fixed imports * refactored getCipherView$ observable * refactored onVaultItemsEvent to switch case * Refactored to use toast service instead of platform utils service for toast * merged with main and fixed conflicts * reordered standalone property * converted components to standalone * cleaned up ng module for org vault * cleaned up vault module individual vault * fixed conflicts * Replaced deprecated toast service * refactored to use switch case for org vault * fixed comments and fixed failing tests reverted to use getAllDecrypted
This commit is contained in:
parent
b32f6182a5
commit
91aa475766
@ -1,5 +1,6 @@
|
|||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { By } from "@angular/platform-browser";
|
import { By } from "@angular/platform-browser";
|
||||||
|
import { RouterTestingModule } from "@angular/router/testing";
|
||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
|||||||
import { BannerComponent, BannerModule } from "@bitwarden/components";
|
import { BannerComponent, BannerModule } from "@bitwarden/components";
|
||||||
|
|
||||||
import { VerifyEmailComponent } from "../../../auth/settings/verify-email.component";
|
import { VerifyEmailComponent } from "../../../auth/settings/verify-email.component";
|
||||||
import { LooseComponentsModule } from "../../../shared";
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
import { VaultBannersService, VisibleVaultBanner } from "./services/vault-banners.service";
|
import { VaultBannersService, VisibleVaultBanner } from "./services/vault-banners.service";
|
||||||
import { VaultBannersComponent } from "./vault-banners.component";
|
import { VaultBannersComponent } from "./vault-banners.component";
|
||||||
@ -36,13 +37,15 @@ describe("VaultBannersComponent", () => {
|
|||||||
bannerService.shouldShowLowKDFBanner.mockResolvedValue(false);
|
bannerService.shouldShowLowKDFBanner.mockResolvedValue(false);
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [BannerModule, LooseComponentsModule, VerifyEmailComponent],
|
imports: [
|
||||||
declarations: [VaultBannersComponent, I18nPipe],
|
BannerModule,
|
||||||
|
SharedModule,
|
||||||
|
VerifyEmailComponent,
|
||||||
|
VaultBannersComponent,
|
||||||
|
RouterTestingModule,
|
||||||
|
],
|
||||||
|
declarations: [I18nPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
|
||||||
provide: VaultBannersService,
|
|
||||||
useValue: bannerService,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: I18nService,
|
provide: I18nService,
|
||||||
useValue: mock<I18nService>({ t: (key) => key }),
|
useValue: mock<I18nService>({ t: (key) => key }),
|
||||||
@ -60,7 +63,9 @@ describe("VaultBannersComponent", () => {
|
|||||||
useValue: mock<TokenService>(),
|
useValue: mock<TokenService>(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
})
|
||||||
|
.overrideProvider(VaultBannersService, { useValue: bannerService })
|
||||||
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { BannerModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { VerifyEmailComponent } from "../../../auth/settings/verify-email.component";
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
import { VaultBannersService, VisibleVaultBanner } from "./services/vault-banners.service";
|
import { VaultBannersService, VisibleVaultBanner } from "./services/vault-banners.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
selector: "app-vault-banners",
|
selector: "app-vault-banners",
|
||||||
templateUrl: "./vault-banners.component.html",
|
templateUrl: "./vault-banners.component.html",
|
||||||
|
imports: [VerifyEmailComponent, SharedModule, BannerModule],
|
||||||
|
providers: [VaultBannersService],
|
||||||
})
|
})
|
||||||
export class VaultBannersComponent implements OnInit {
|
export class VaultBannersComponent implements OnInit {
|
||||||
visibleBanners: VisibleVaultBanner[] = [];
|
visibleBanners: VisibleVaultBanner[] = [];
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
@ -8,14 +9,19 @@ import {
|
|||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
|
import { BreadcrumbsModule, MenuModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { HeaderModule } from "../../../layouts/header/header.module";
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
import { CollectionDialogTabType } from "../../components/collection-dialog";
|
import { CollectionDialogTabType } from "../../components/collection-dialog";
|
||||||
|
import { PipesModule } from "../pipes/pipes.module";
|
||||||
import {
|
import {
|
||||||
All,
|
All,
|
||||||
RoutedVaultFilterModel,
|
RoutedVaultFilterModel,
|
||||||
@ -23,8 +29,18 @@ import {
|
|||||||
} from "../vault-filter/shared/models/routed-vault-filter.model";
|
} from "../vault-filter/shared/models/routed-vault-filter.model";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
selector: "app-vault-header",
|
selector: "app-vault-header",
|
||||||
templateUrl: "./vault-header.component.html",
|
templateUrl: "./vault-header.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MenuModule,
|
||||||
|
SharedModule,
|
||||||
|
BreadcrumbsModule,
|
||||||
|
HeaderModule,
|
||||||
|
PipesModule,
|
||||||
|
JslibModule,
|
||||||
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class VaultHeaderComponent implements OnInit {
|
export class VaultHeaderComponent implements OnInit {
|
||||||
|
@ -158,10 +158,9 @@ describe("VaultOnboardingComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should set installExtension to true when hasBWInstalled command is passed", async () => {
|
it("should set installExtension to true when hasBWInstalled command is passed", async () => {
|
||||||
const saveCompletedTasksSpy = jest.spyOn(
|
const saveCompletedTasksSpy = jest
|
||||||
(component as any).vaultOnboardingService,
|
.spyOn((component as any).vaultOnboardingService, "setVaultOnboardingTasks")
|
||||||
"setVaultOnboardingTasks",
|
.mockReturnValue(Promise.resolve());
|
||||||
);
|
|
||||||
|
|
||||||
(component as any).vaultOnboardingService.vaultOnboardingState$ = of({
|
(component as any).vaultOnboardingService.vaultOnboardingState$ = of({
|
||||||
createAccount: true,
|
createAccount: true,
|
||||||
|
@ -24,11 +24,17 @@ import { LinkModule } from "@bitwarden/components";
|
|||||||
import { OnboardingModule } from "../../../shared/components/onboarding/onboarding.module";
|
import { OnboardingModule } from "../../../shared/components/onboarding/onboarding.module";
|
||||||
|
|
||||||
import { VaultOnboardingService as VaultOnboardingServiceAbstraction } from "./services/abstraction/vault-onboarding.service";
|
import { VaultOnboardingService as VaultOnboardingServiceAbstraction } from "./services/abstraction/vault-onboarding.service";
|
||||||
import { VaultOnboardingTasks } from "./services/vault-onboarding.service";
|
import { VaultOnboardingService, VaultOnboardingTasks } from "./services/vault-onboarding.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [OnboardingModule, CommonModule, JslibModule, LinkModule],
|
imports: [OnboardingModule, CommonModule, JslibModule, LinkModule],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: VaultOnboardingServiceAbstraction,
|
||||||
|
useClass: VaultOnboardingService,
|
||||||
|
},
|
||||||
|
],
|
||||||
selector: "app-vault-onboarding",
|
selector: "app-vault-onboarding",
|
||||||
templateUrl: "vault-onboarding.component.html",
|
templateUrl: "vault-onboarding.component.html",
|
||||||
})
|
})
|
||||||
|
@ -60,6 +60,7 @@ import { ServiceUtils } from "@bitwarden/common/vault/service-utils";
|
|||||||
import { DialogService, Icons, ToastService } from "@bitwarden/components";
|
import { DialogService, Icons, ToastService } from "@bitwarden/components";
|
||||||
import { CollectionAssignmentResult, PasswordRepromptService } from "@bitwarden/vault";
|
import { CollectionAssignmentResult, PasswordRepromptService } from "@bitwarden/vault";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../shared/shared.module";
|
||||||
import { AssignCollectionsWebComponent } from "../components/assign-collections";
|
import { AssignCollectionsWebComponent } from "../components/assign-collections";
|
||||||
import {
|
import {
|
||||||
CollectionDialogAction,
|
CollectionDialogAction,
|
||||||
@ -68,6 +69,7 @@ import {
|
|||||||
} from "../components/collection-dialog";
|
} from "../components/collection-dialog";
|
||||||
import { VaultItem } from "../components/vault-items/vault-item";
|
import { VaultItem } from "../components/vault-items/vault-item";
|
||||||
import { VaultItemEvent } from "../components/vault-items/vault-item-event";
|
import { VaultItemEvent } from "../components/vault-items/vault-item-event";
|
||||||
|
import { VaultItemsModule } from "../components/vault-items/vault-items.module";
|
||||||
import { getNestedCollectionTree } from "../utils/collection-utils";
|
import { getNestedCollectionTree } from "../utils/collection-utils";
|
||||||
|
|
||||||
import { AddEditComponent } from "./add-edit.component";
|
import { AddEditComponent } from "./add-edit.component";
|
||||||
@ -87,6 +89,7 @@ import {
|
|||||||
import { openIndividualVaultCollectionsDialog } from "./collections.component";
|
import { openIndividualVaultCollectionsDialog } from "./collections.component";
|
||||||
import { FolderAddEditDialogResult, openFolderAddEditDialog } from "./folder-add-edit.component";
|
import { FolderAddEditDialogResult, openFolderAddEditDialog } from "./folder-add-edit.component";
|
||||||
import { ShareComponent } from "./share.component";
|
import { ShareComponent } from "./share.component";
|
||||||
|
import { VaultBannersComponent } from "./vault-banners/vault-banners.component";
|
||||||
import { VaultFilterComponent } from "./vault-filter/components/vault-filter.component";
|
import { VaultFilterComponent } from "./vault-filter/components/vault-filter.component";
|
||||||
import { VaultFilterService } from "./vault-filter/services/abstractions/vault-filter.service";
|
import { VaultFilterService } from "./vault-filter/services/abstractions/vault-filter.service";
|
||||||
import { RoutedVaultFilterBridgeService } from "./vault-filter/services/routed-vault-filter-bridge.service";
|
import { RoutedVaultFilterBridgeService } from "./vault-filter/services/routed-vault-filter-bridge.service";
|
||||||
@ -99,13 +102,25 @@ import {
|
|||||||
} from "./vault-filter/shared/models/routed-vault-filter.model";
|
} from "./vault-filter/shared/models/routed-vault-filter.model";
|
||||||
import { VaultFilter } from "./vault-filter/shared/models/vault-filter.model";
|
import { VaultFilter } from "./vault-filter/shared/models/vault-filter.model";
|
||||||
import { FolderFilter, OrganizationFilter } from "./vault-filter/shared/models/vault-filter.type";
|
import { FolderFilter, OrganizationFilter } from "./vault-filter/shared/models/vault-filter.type";
|
||||||
|
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
||||||
|
import { VaultHeaderComponent } from "./vault-header/vault-header.component";
|
||||||
|
import { VaultOnboardingComponent } from "./vault-onboarding/vault-onboarding.component";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = "VaultComponent";
|
const BroadcasterSubscriptionId = "VaultComponent";
|
||||||
const SearchTextDebounceInterval = 200;
|
const SearchTextDebounceInterval = 200;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
selector: "app-vault",
|
selector: "app-vault",
|
||||||
templateUrl: "vault.component.html",
|
templateUrl: "vault.component.html",
|
||||||
|
imports: [
|
||||||
|
VaultHeaderComponent,
|
||||||
|
VaultOnboardingComponent,
|
||||||
|
VaultBannersComponent,
|
||||||
|
VaultFilterModule,
|
||||||
|
VaultItemsModule,
|
||||||
|
SharedModule,
|
||||||
|
],
|
||||||
providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService],
|
providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService],
|
||||||
})
|
})
|
||||||
export class VaultComponent implements OnInit, OnDestroy {
|
export class VaultComponent implements OnInit, OnDestroy {
|
||||||
@ -323,18 +338,14 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
const cipherId = getCipherIdFromParams(params);
|
const cipherId = getCipherIdFromParams(params);
|
||||||
if (cipherId) {
|
if (cipherId) {
|
||||||
if ((await this.cipherService.get(cipherId)) != null) {
|
if ((await this.cipherService.get(cipherId)) != null) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.editCipherId(cipherId);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.editCipherId(cipherId);
|
|
||||||
} else {
|
} else {
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"error",
|
variant: "error",
|
||||||
null,
|
title: null,
|
||||||
this.i18nService.t("unknownCipher"),
|
message: this.i18nService.t("unknownCipher"),
|
||||||
);
|
});
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.router.navigate([], {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate([], {
|
|
||||||
queryParams: { itemId: null, cipherId: null },
|
queryParams: { itemId: null, cipherId: null },
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
});
|
});
|
||||||
@ -403,36 +414,48 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
async onVaultItemsEvent(event: VaultItemEvent) {
|
async onVaultItemsEvent(event: VaultItemEvent) {
|
||||||
this.processingEvent = true;
|
this.processingEvent = true;
|
||||||
try {
|
try {
|
||||||
if (event.type === "viewAttachments") {
|
switch (event.type) {
|
||||||
await this.editCipherAttachments(event.item);
|
case "viewAttachments":
|
||||||
} else if (event.type === "viewCipherCollections") {
|
await this.editCipherAttachments(event.item);
|
||||||
await this.editCipherCollections(event.item);
|
break;
|
||||||
} else if (event.type === "clone") {
|
case "viewCipherCollections":
|
||||||
await this.cloneCipher(event.item);
|
await this.editCipherCollections(event.item);
|
||||||
} else if (event.type === "restore") {
|
break;
|
||||||
if (event.items.length === 1) {
|
case "clone":
|
||||||
await this.restore(event.items[0]);
|
await this.cloneCipher(event.item);
|
||||||
} else {
|
break;
|
||||||
await this.bulkRestore(event.items);
|
case "restore":
|
||||||
}
|
if (event.items.length === 1) {
|
||||||
} else if (event.type === "delete") {
|
await this.restore(event.items[0]);
|
||||||
await this.handleDeleteEvent(event.items);
|
} else {
|
||||||
} else if (event.type === "moveToFolder") {
|
await this.bulkRestore(event.items);
|
||||||
await this.bulkMove(event.items);
|
}
|
||||||
} else if (event.type === "moveToOrganization") {
|
break;
|
||||||
if (event.items.length === 1) {
|
case "delete":
|
||||||
await this.shareCipher(event.items[0]);
|
await this.handleDeleteEvent(event.items);
|
||||||
} else {
|
break;
|
||||||
await this.bulkShare(event.items);
|
case "moveToFolder":
|
||||||
}
|
await this.bulkMove(event.items);
|
||||||
} else if (event.type === "copyField") {
|
break;
|
||||||
await this.copy(event.item, event.field);
|
case "moveToOrganization":
|
||||||
} else if (event.type === "editCollection") {
|
if (event.items.length === 1) {
|
||||||
await this.editCollection(event.item, CollectionDialogTabType.Info);
|
await this.shareCipher(event.items[0]);
|
||||||
} else if (event.type === "viewCollectionAccess") {
|
} else {
|
||||||
await this.editCollection(event.item, CollectionDialogTabType.Access);
|
await this.bulkShare(event.items);
|
||||||
} else if (event.type === "assignToCollections") {
|
}
|
||||||
await this.bulkAssignToCollections(event.items);
|
break;
|
||||||
|
case "copyField":
|
||||||
|
await this.copy(event.item, event.field);
|
||||||
|
break;
|
||||||
|
case "editCollection":
|
||||||
|
await this.editCollection(event.item, CollectionDialogTabType.Info);
|
||||||
|
break;
|
||||||
|
case "viewCollectionAccess":
|
||||||
|
await this.editCollection(event.item, CollectionDialogTabType.Access);
|
||||||
|
break;
|
||||||
|
case "assignToCollections":
|
||||||
|
await this.bulkAssignToCollections(event.items);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.processingEvent = false;
|
this.processingEvent = false;
|
||||||
@ -445,9 +468,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
const orgs = await firstValueFrom(this.filterComponent.filters.organizationFilter.data$);
|
const orgs = await firstValueFrom(this.filterComponent.filters.organizationFilter.data$);
|
||||||
const orgNode = ServiceUtils.getTreeNodeObject(orgs, orgId) as TreeNode<OrganizationFilter>;
|
const orgNode = ServiceUtils.getTreeNodeObject(orgs, orgId) as TreeNode<OrganizationFilter>;
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.filterComponent.filters?.organizationFilter?.action(orgNode);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.filterComponent.filters?.organizationFilter?.action(orgNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addFolder = async (): Promise<void> => {
|
addFolder = async (): Promise<void> => {
|
||||||
@ -670,7 +691,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
// Navigate away if we deleted the collection we were viewing
|
// Navigate away if we deleted the collection we were viewing
|
||||||
if (this.selectedCollection?.node.id === c?.id) {
|
if (this.selectedCollection?.node.id === c?.id) {
|
||||||
void this.router.navigate([], {
|
await this.router.navigate([], {
|
||||||
queryParams: { collectionId: this.selectedCollection.parent?.node.id ?? null },
|
queryParams: { collectionId: this.selectedCollection.parent?.node.id ?? null },
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
replaceUrl: true,
|
replaceUrl: true,
|
||||||
@ -697,14 +718,15 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
try {
|
try {
|
||||||
await this.apiService.deleteCollection(collection.organizationId, collection.id);
|
await this.apiService.deleteCollection(collection.organizationId, collection.id);
|
||||||
await this.collectionService.delete(collection.id);
|
await this.collectionService.delete(collection.id);
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"success",
|
this.toastService.showToast({
|
||||||
null,
|
variant: "success",
|
||||||
this.i18nService.t("deletedCollectionId", collection.name),
|
title: null,
|
||||||
);
|
message: this.i18nService.t("deletedCollectionId", collection.name),
|
||||||
|
});
|
||||||
// Navigate away if we deleted the collection we were viewing
|
// Navigate away if we deleted the collection we were viewing
|
||||||
if (this.selectedCollection?.node.id === collection.id) {
|
if (this.selectedCollection?.node.id === collection.id) {
|
||||||
void this.router.navigate([], {
|
await this.router.navigate([], {
|
||||||
queryParams: { collectionId: this.selectedCollection.parent?.node.id ?? null },
|
queryParams: { collectionId: this.selectedCollection.parent?.node.id ?? null },
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
replaceUrl: true,
|
replaceUrl: true,
|
||||||
@ -722,11 +744,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ciphers.length === 0) {
|
if (ciphers.length === 0) {
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"error",
|
variant: "error",
|
||||||
this.i18nService.t("errorOccurred"),
|
title: this.i18nService.t("errorOccurred"),
|
||||||
this.i18nService.t("nothingSelected"),
|
message: this.i18nService.t("nothingSelected"),
|
||||||
);
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -790,7 +812,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.cipherService.restoreWithServer(c.id);
|
await this.cipherService.restoreWithServer(c.id);
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem"));
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("restoredItem"),
|
||||||
|
});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
@ -809,12 +835,20 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const selectedCipherIds = ciphers.map((cipher) => cipher.id);
|
const selectedCipherIds = ciphers.map((cipher) => cipher.id);
|
||||||
if (selectedCipherIds.length === 0) {
|
if (selectedCipherIds.length === 0) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("nothingSelected"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("nothingSelected"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.cipherService.restoreManyWithServer(selectedCipherIds);
|
await this.cipherService.restoreManyWithServer(selectedCipherIds);
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItems"));
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("restoredItems"),
|
||||||
|
});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,11 +896,12 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.deleteCipherWithServer(c.id, permanent);
|
await this.deleteCipherWithServer(c.id, permanent);
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"success",
|
this.toastService.showToast({
|
||||||
null,
|
variant: "success",
|
||||||
this.i18nService.t(permanent ? "permanentlyDeletedItem" : "deletedItem"),
|
title: null,
|
||||||
);
|
message: this.i18nService.t(permanent ? "permanentlyDeletedItem" : "deletedItem"),
|
||||||
|
});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
@ -883,7 +918,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ciphers.length === 0 && collections.length === 0) {
|
if (ciphers.length === 0 && collections.length === 0) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("nothingSelected"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("nothingSelected"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -926,7 +965,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const selectedCipherIds = ciphers.map((cipher) => cipher.id);
|
const selectedCipherIds = ciphers.map((cipher) => cipher.id);
|
||||||
if (selectedCipherIds.length === 0) {
|
if (selectedCipherIds.length === 0) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("nothingSelected"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("nothingSelected"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,7 +1001,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
value = await this.totpService.getCode(cipher.login.totp);
|
value = await this.totpService.getCode(cipher.login.totp);
|
||||||
typeI18nKey = "verificationCodeTotp";
|
typeI18nKey = "verificationCodeTotp";
|
||||||
} else {
|
} else {
|
||||||
this.platformUtilsService.showToast("info", null, this.i18nService.t("unexpectedError"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("unexpectedError"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -974,20 +1021,19 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.platformUtilsService.copyToClipboard(value, { window: window });
|
this.platformUtilsService.copyToClipboard(value, { window: window });
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"info",
|
variant: "info",
|
||||||
null,
|
title: null,
|
||||||
this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
|
message: this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
|
||||||
);
|
});
|
||||||
|
|
||||||
if (field === "password") {
|
if (field === "password") {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
|
|
||||||
} else if (field === "totp") {
|
} else if (field === "totp") {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.eventCollectionService.collect(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
EventType.Cipher_ClientCopiedHiddenField,
|
||||||
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedHiddenField, cipher.id);
|
cipher.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1006,7 +1052,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ciphers.length === 0) {
|
if (ciphers.length === 0) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("nothingSelected"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("nothingSelected"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,9 +1106,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
void this.router.navigate([], {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate([], {
|
|
||||||
relativeTo: this.route,
|
relativeTo: this.route,
|
||||||
queryParams: queryParams,
|
queryParams: queryParams,
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
|
@ -1,30 +1,18 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { BannerModule, BreadcrumbsModule } from "@bitwarden/components";
|
|
||||||
|
|
||||||
import { VerifyEmailComponent } from "../../auth/settings/verify-email.component";
|
|
||||||
import { LooseComponentsModule, SharedModule } from "../../shared";
|
import { LooseComponentsModule, SharedModule } from "../../shared";
|
||||||
import { CollectionDialogModule } from "../components/collection-dialog";
|
import { CollectionDialogModule } from "../components/collection-dialog";
|
||||||
import { VaultItemsModule } from "../components/vault-items/vault-items.module";
|
|
||||||
import { CollectionBadgeModule } from "../org-vault/collection-badge/collection-badge.module";
|
import { CollectionBadgeModule } from "../org-vault/collection-badge/collection-badge.module";
|
||||||
import { GroupBadgeModule } from "../org-vault/group-badge/group-badge.module";
|
import { GroupBadgeModule } from "../org-vault/group-badge/group-badge.module";
|
||||||
|
|
||||||
import { BulkDialogsModule } from "./bulk-action-dialogs/bulk-dialogs.module";
|
import { BulkDialogsModule } from "./bulk-action-dialogs/bulk-dialogs.module";
|
||||||
import { OrganizationBadgeModule } from "./organization-badge/organization-badge.module";
|
import { OrganizationBadgeModule } from "./organization-badge/organization-badge.module";
|
||||||
import { PipesModule } from "./pipes/pipes.module";
|
import { PipesModule } from "./pipes/pipes.module";
|
||||||
import { VaultBannersService } from "./vault-banners/services/vault-banners.service";
|
|
||||||
import { VaultBannersComponent } from "./vault-banners/vault-banners.component";
|
|
||||||
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
|
||||||
import { VaultHeaderComponent } from "./vault-header/vault-header.component";
|
|
||||||
import { VaultOnboardingService as VaultOnboardingServiceAbstraction } from "./vault-onboarding/services/abstraction/vault-onboarding.service";
|
|
||||||
import { VaultOnboardingService } from "./vault-onboarding/services/vault-onboarding.service";
|
|
||||||
import { VaultOnboardingComponent } from "./vault-onboarding/vault-onboarding.component";
|
|
||||||
import { VaultRoutingModule } from "./vault-routing.module";
|
import { VaultRoutingModule } from "./vault-routing.module";
|
||||||
import { VaultComponent } from "./vault.component";
|
import { VaultComponent } from "./vault.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
VaultFilterModule,
|
|
||||||
VaultRoutingModule,
|
VaultRoutingModule,
|
||||||
OrganizationBadgeModule,
|
OrganizationBadgeModule,
|
||||||
GroupBadgeModule,
|
GroupBadgeModule,
|
||||||
@ -33,21 +21,8 @@ import { VaultComponent } from "./vault.component";
|
|||||||
SharedModule,
|
SharedModule,
|
||||||
LooseComponentsModule,
|
LooseComponentsModule,
|
||||||
BulkDialogsModule,
|
BulkDialogsModule,
|
||||||
BreadcrumbsModule,
|
|
||||||
VaultItemsModule,
|
|
||||||
CollectionDialogModule,
|
CollectionDialogModule,
|
||||||
VaultOnboardingComponent,
|
VaultComponent,
|
||||||
BannerModule,
|
|
||||||
VerifyEmailComponent,
|
|
||||||
],
|
|
||||||
declarations: [VaultComponent, VaultHeaderComponent, VaultBannersComponent],
|
|
||||||
exports: [VaultComponent],
|
|
||||||
providers: [
|
|
||||||
VaultBannersService,
|
|
||||||
{
|
|
||||||
provide: VaultOnboardingServiceAbstraction,
|
|
||||||
useClass: VaultOnboardingService,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class VaultModule {}
|
export class VaultModule {}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
@ -9,8 +11,16 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||||
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
|
import {
|
||||||
|
DialogService,
|
||||||
|
SimpleDialogOptions,
|
||||||
|
BreadcrumbsModule,
|
||||||
|
MenuModule,
|
||||||
|
SearchModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { HeaderModule } from "../../../layouts/header/header.module";
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view";
|
import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view";
|
||||||
import { CollectionDialogTabType } from "../../components/collection-dialog";
|
import { CollectionDialogTabType } from "../../components/collection-dialog";
|
||||||
import { CollectionAdminService } from "../../core/collection-admin.service";
|
import { CollectionAdminService } from "../../core/collection-admin.service";
|
||||||
@ -21,8 +31,18 @@ import {
|
|||||||
} from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
} from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
selector: "app-org-vault-header",
|
selector: "app-org-vault-header",
|
||||||
templateUrl: "./vault-header.component.html",
|
templateUrl: "./vault-header.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MenuModule,
|
||||||
|
SharedModule,
|
||||||
|
BreadcrumbsModule,
|
||||||
|
HeaderModule,
|
||||||
|
SearchModule,
|
||||||
|
JslibModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class VaultHeaderComponent implements OnInit {
|
export class VaultHeaderComponent implements OnInit {
|
||||||
protected All = All;
|
protected All = All;
|
||||||
|
@ -58,11 +58,12 @@ import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
|||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
import { ServiceUtils } from "@bitwarden/common/vault/service-utils";
|
import { ServiceUtils } from "@bitwarden/common/vault/service-utils";
|
||||||
import { DialogService, Icons, ToastService } from "@bitwarden/components";
|
import { DialogService, Icons, NoItemsModule, ToastService } from "@bitwarden/components";
|
||||||
import { CollectionAssignmentResult, PasswordRepromptService } from "@bitwarden/vault";
|
import { CollectionAssignmentResult, PasswordRepromptService } from "@bitwarden/vault";
|
||||||
|
|
||||||
import { GroupService, GroupView } from "../../admin-console/organizations/core";
|
import { GroupService, GroupView } from "../../admin-console/organizations/core";
|
||||||
import { openEntityEventsDialog } from "../../admin-console/organizations/manage/entity-events.component";
|
import { openEntityEventsDialog } from "../../admin-console/organizations/manage/entity-events.component";
|
||||||
|
import { SharedModule } from "../../shared";
|
||||||
import { VaultFilterService } from "../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service";
|
import { VaultFilterService } from "../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service";
|
||||||
import { VaultFilter } from "../../vault/individual-vault/vault-filter/shared/models/vault-filter.model";
|
import { VaultFilter } from "../../vault/individual-vault/vault-filter/shared/models/vault-filter.model";
|
||||||
import { AssignCollectionsWebComponent } from "../components/assign-collections";
|
import { AssignCollectionsWebComponent } from "../components/assign-collections";
|
||||||
@ -72,6 +73,7 @@ import {
|
|||||||
openCollectionDialog,
|
openCollectionDialog,
|
||||||
} from "../components/collection-dialog";
|
} from "../components/collection-dialog";
|
||||||
import { VaultItemEvent } from "../components/vault-items/vault-item-event";
|
import { VaultItemEvent } from "../components/vault-items/vault-item-event";
|
||||||
|
import { VaultItemsModule } from "../components/vault-items/vault-items.module";
|
||||||
import { CollectionAdminService } from "../core/collection-admin.service";
|
import { CollectionAdminService } from "../core/collection-admin.service";
|
||||||
import { CollectionAdminView } from "../core/views/collection-admin.view";
|
import { CollectionAdminView } from "../core/views/collection-admin.view";
|
||||||
import {
|
import {
|
||||||
@ -87,6 +89,7 @@ import {
|
|||||||
RoutedVaultFilterModel,
|
RoutedVaultFilterModel,
|
||||||
Unassigned,
|
Unassigned,
|
||||||
} from "../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
} from "../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
||||||
|
import { VaultHeaderComponent } from "../org-vault/vault-header/vault-header.component";
|
||||||
import { getNestedCollectionTree } from "../utils/collection-utils";
|
import { getNestedCollectionTree } from "../utils/collection-utils";
|
||||||
|
|
||||||
import { AddEditComponent } from "./add-edit.component";
|
import { AddEditComponent } from "./add-edit.component";
|
||||||
@ -95,7 +98,9 @@ import {
|
|||||||
BulkCollectionsDialogComponent,
|
BulkCollectionsDialogComponent,
|
||||||
BulkCollectionsDialogResult,
|
BulkCollectionsDialogResult,
|
||||||
} from "./bulk-collections-dialog";
|
} from "./bulk-collections-dialog";
|
||||||
|
import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component";
|
||||||
import { openOrgVaultCollectionsDialog } from "./collections.component";
|
import { openOrgVaultCollectionsDialog } from "./collections.component";
|
||||||
|
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = "OrgVaultComponent";
|
const BroadcasterSubscriptionId = "OrgVaultComponent";
|
||||||
const SearchTextDebounceInterval = 200;
|
const SearchTextDebounceInterval = 200;
|
||||||
@ -106,8 +111,17 @@ enum AddAccessStatusType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
selector: "app-org-vault",
|
selector: "app-org-vault",
|
||||||
templateUrl: "vault.component.html",
|
templateUrl: "vault.component.html",
|
||||||
|
imports: [
|
||||||
|
VaultHeaderComponent,
|
||||||
|
CollectionAccessRestrictedComponent,
|
||||||
|
VaultFilterModule,
|
||||||
|
VaultItemsModule,
|
||||||
|
SharedModule,
|
||||||
|
NoItemsModule,
|
||||||
|
],
|
||||||
providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService],
|
providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService],
|
||||||
})
|
})
|
||||||
export class VaultComponent implements OnInit, OnDestroy {
|
export class VaultComponent implements OnInit, OnDestroy {
|
||||||
@ -577,7 +591,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
if (canEditCipher) {
|
if (canEditCipher) {
|
||||||
await this.editCipherId(cipherId);
|
await this.editCipherId(cipherId);
|
||||||
} else {
|
} else {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("unknownCipher"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("unknownCipher"),
|
||||||
|
});
|
||||||
await this.router.navigate([], {
|
await this.router.navigate([], {
|
||||||
queryParams: { cipherId: null, itemId: null },
|
queryParams: { cipherId: null, itemId: null },
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
@ -598,14 +616,14 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
const cipher = allCiphers$.find((c) => c.id === cipherId);
|
const cipher = allCiphers$.find((c) => c.id === cipherId);
|
||||||
if (organization.useEvents && cipher != undefined) {
|
if (organization.useEvents && cipher != undefined) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.viewEvents(cipher);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.viewEvents(cipher);
|
|
||||||
} else {
|
} else {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("unknownCipher"));
|
this.toastService.showToast({
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
variant: "error",
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
title: null,
|
||||||
this.router.navigate([], {
|
message: this.i18nService.t("unknownCipher"),
|
||||||
|
});
|
||||||
|
await this.router.navigate([], {
|
||||||
queryParams: { viewEvents: null },
|
queryParams: { viewEvents: null },
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
});
|
});
|
||||||
@ -686,50 +704,65 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.processingEvent = true;
|
this.processingEvent = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (event.type === "viewAttachments") {
|
switch (event.type) {
|
||||||
await this.editCipherAttachments(event.item);
|
case "viewAttachments":
|
||||||
} else if (event.type === "viewCipherCollections") {
|
await this.editCipherAttachments(event.item);
|
||||||
await this.editCipherCollections(event.item);
|
break;
|
||||||
} else if (event.type === "clone") {
|
case "viewCipherCollections":
|
||||||
await this.cloneCipher(event.item);
|
await this.editCipherCollections(event.item);
|
||||||
} else if (event.type === "restore") {
|
break;
|
||||||
if (event.items.length === 1) {
|
case "clone":
|
||||||
await this.restore(event.items[0]);
|
await this.cloneCipher(event.item);
|
||||||
} else {
|
break;
|
||||||
await this.bulkRestore(event.items);
|
case "restore":
|
||||||
|
if (event.items.length === 1) {
|
||||||
|
await this.restore(event.items[0]);
|
||||||
|
} else {
|
||||||
|
await this.bulkRestore(event.items);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "delete": {
|
||||||
|
const ciphers = event.items
|
||||||
|
.filter((i) => i.collection === undefined)
|
||||||
|
.map((i) => i.cipher);
|
||||||
|
const collections = event.items
|
||||||
|
.filter((i) => i.cipher === undefined)
|
||||||
|
.map((i) => i.collection);
|
||||||
|
if (ciphers.length === 1 && collections.length === 0) {
|
||||||
|
await this.deleteCipher(ciphers[0]);
|
||||||
|
} else if (ciphers.length === 0 && collections.length === 1) {
|
||||||
|
await this.deleteCollection(collections[0] as CollectionAdminView);
|
||||||
|
} else {
|
||||||
|
await this.bulkDelete(ciphers, collections, this.organization);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if (event.type === "delete") {
|
case "copyField":
|
||||||
const ciphers = event.items.filter((i) => i.collection === undefined).map((i) => i.cipher);
|
await this.copy(event.item, event.field);
|
||||||
const collections = event.items
|
break;
|
||||||
.filter((i) => i.cipher === undefined)
|
case "editCollection":
|
||||||
.map((i) => i.collection);
|
await this.editCollection(
|
||||||
if (ciphers.length === 1 && collections.length === 0) {
|
event.item as CollectionAdminView,
|
||||||
await this.deleteCipher(ciphers[0]);
|
CollectionDialogTabType.Info,
|
||||||
} else if (ciphers.length === 0 && collections.length === 1) {
|
event.readonly,
|
||||||
await this.deleteCollection(collections[0] as CollectionAdminView);
|
);
|
||||||
} else {
|
break;
|
||||||
await this.bulkDelete(ciphers, collections, this.organization);
|
case "viewCollectionAccess":
|
||||||
}
|
await this.editCollection(
|
||||||
} else if (event.type === "copyField") {
|
event.item as CollectionAdminView,
|
||||||
await this.copy(event.item, event.field);
|
CollectionDialogTabType.Access,
|
||||||
} else if (event.type === "editCollection") {
|
event.readonly,
|
||||||
await this.editCollection(
|
);
|
||||||
event.item as CollectionAdminView,
|
break;
|
||||||
CollectionDialogTabType.Info,
|
case "bulkEditCollectionAccess":
|
||||||
event.readonly,
|
await this.bulkEditCollectionAccess(event.items, this.organization);
|
||||||
);
|
break;
|
||||||
} else if (event.type === "viewCollectionAccess") {
|
case "assignToCollections":
|
||||||
await this.editCollection(
|
await this.bulkAssignToCollections(event.items);
|
||||||
event.item as CollectionAdminView,
|
break;
|
||||||
CollectionDialogTabType.Access,
|
case "viewEvents":
|
||||||
event.readonly,
|
await this.viewEvents(event.item);
|
||||||
);
|
break;
|
||||||
} else if (event.type === "bulkEditCollectionAccess") {
|
|
||||||
await this.bulkEditCollectionAccess(event.items, this.organization);
|
|
||||||
} else if (event.type === "assignToCollections") {
|
|
||||||
await this.bulkAssignToCollections(event.items);
|
|
||||||
} else if (event.type === "viewEvents") {
|
|
||||||
await this.viewEvents(event.item);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.processingEvent = false;
|
this.processingEvent = false;
|
||||||
@ -962,7 +995,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.organization?.canEditAnyCollection(this.flexibleCollectionsV1Enabled) ||
|
this.organization?.canEditAnyCollection(this.flexibleCollectionsV1Enabled) ||
|
||||||
c.isUnassigned;
|
c.isUnassigned;
|
||||||
await this.cipherService.restoreWithServer(c.id, asAdmin);
|
await this.cipherService.restoreWithServer(c.id, asAdmin);
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem"));
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("restoredItem"),
|
||||||
|
});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
@ -1008,11 +1045,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unassignedCiphers.length === 0 && editAccessCiphers.length === 0) {
|
if (unassignedCiphers.length === 0 && editAccessCiphers.length === 0) {
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"error",
|
variant: "error",
|
||||||
this.i18nService.t("errorOccurred"),
|
title: this.i18nService.t("errorOccurred"),
|
||||||
this.i18nService.t("nothingSelected"),
|
message: this.i18nService.t("nothingSelected"),
|
||||||
);
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1023,7 +1060,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItems"));
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("restoredItems"),
|
||||||
|
});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,11 +1099,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.deleteCipherWithServer(c.id, permanent, c.isUnassigned);
|
await this.deleteCipherWithServer(c.id, permanent, c.isUnassigned);
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"success",
|
variant: "success",
|
||||||
null,
|
title: null,
|
||||||
this.i18nService.t(permanent ? "permanentlyDeletedItem" : "deletedItem"),
|
message: this.i18nService.t(permanent ? "permanentlyDeletedItem" : "deletedItem"),
|
||||||
);
|
});
|
||||||
this.refresh();
|
this.refresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
@ -1085,11 +1126,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.apiService.deleteCollection(this.organization?.id, collection.id);
|
await this.apiService.deleteCollection(this.organization?.id, collection.id);
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"success",
|
variant: "success",
|
||||||
null,
|
title: null,
|
||||||
this.i18nService.t("deletedCollectionId", collection.name),
|
message: this.i18nService.t("deletedCollectionId", collection.name),
|
||||||
);
|
});
|
||||||
|
|
||||||
// Navigate away if we deleted the collection we were viewing
|
// Navigate away if we deleted the collection we were viewing
|
||||||
if (this.selectedCollection?.node.id === collection.id) {
|
if (this.selectedCollection?.node.id === collection.id) {
|
||||||
@ -1128,7 +1169,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (ciphers.length === 0 && collections.length === 0) {
|
if (ciphers.length === 0 && collections.length === 0) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("nothingSelected"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("nothingSelected"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1182,7 +1227,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
value = await this.totpService.getCode(cipher.login.totp);
|
value = await this.totpService.getCode(cipher.login.totp);
|
||||||
typeI18nKey = "verificationCodeTotp";
|
typeI18nKey = "verificationCodeTotp";
|
||||||
} else {
|
} else {
|
||||||
this.platformUtilsService.showToast("info", null, this.i18nService.t("unexpectedError"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("unexpectedError"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1198,20 +1247,19 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.platformUtilsService.copyToClipboard(value, { window: window });
|
this.platformUtilsService.copyToClipboard(value, { window: window });
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"info",
|
variant: "info",
|
||||||
null,
|
title: null,
|
||||||
this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
|
message: this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)),
|
||||||
);
|
});
|
||||||
|
|
||||||
if (field === "password") {
|
if (field === "password") {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
|
|
||||||
} else if (field === "totp") {
|
} else if (field === "totp") {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.eventCollectionService.collect(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
EventType.Cipher_ClientCopiedHiddenField,
|
||||||
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedHiddenField, cipher.id);
|
cipher.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1310,7 +1358,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async bulkAssignToCollections(items: CipherView[]) {
|
async bulkAssignToCollections(items: CipherView[]) {
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("nothingSelected"));
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("nothingSelected"),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1381,9 +1433,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
void this.router.navigate([], {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate([], {
|
|
||||||
relativeTo: this.route,
|
relativeTo: this.route,
|
||||||
queryParams: queryParams,
|
queryParams: queryParams,
|
||||||
queryParamsHandling: "merge",
|
queryParamsHandling: "merge",
|
||||||
|
@ -1,40 +1,25 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { BreadcrumbsModule, NoItemsModule, SearchModule } from "@bitwarden/components";
|
|
||||||
|
|
||||||
import { LooseComponentsModule } from "../../shared/loose-components.module";
|
import { LooseComponentsModule } from "../../shared/loose-components.module";
|
||||||
import { SharedModule } from "../../shared/shared.module";
|
import { SharedModule } from "../../shared/shared.module";
|
||||||
import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module";
|
import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module";
|
||||||
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
|
|
||||||
import { CollectionDialogModule } from "../components/collection-dialog";
|
import { CollectionDialogModule } from "../components/collection-dialog";
|
||||||
import { VaultItemsModule } from "../components/vault-items/vault-items.module";
|
|
||||||
|
|
||||||
import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component";
|
|
||||||
import { CollectionBadgeModule } from "./collection-badge/collection-badge.module";
|
import { CollectionBadgeModule } from "./collection-badge/collection-badge.module";
|
||||||
import { GroupBadgeModule } from "./group-badge/group-badge.module";
|
import { GroupBadgeModule } from "./group-badge/group-badge.module";
|
||||||
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
|
||||||
import { VaultHeaderComponent } from "./vault-header/vault-header.component";
|
|
||||||
import { VaultRoutingModule } from "./vault-routing.module";
|
import { VaultRoutingModule } from "./vault-routing.module";
|
||||||
import { VaultComponent } from "./vault.component";
|
import { VaultComponent } from "./vault.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
VaultRoutingModule,
|
VaultRoutingModule,
|
||||||
VaultFilterModule,
|
|
||||||
SharedModule,
|
SharedModule,
|
||||||
LooseComponentsModule,
|
LooseComponentsModule,
|
||||||
GroupBadgeModule,
|
GroupBadgeModule,
|
||||||
CollectionBadgeModule,
|
CollectionBadgeModule,
|
||||||
OrganizationBadgeModule,
|
OrganizationBadgeModule,
|
||||||
PipesModule,
|
|
||||||
BreadcrumbsModule,
|
|
||||||
VaultItemsModule,
|
|
||||||
CollectionDialogModule,
|
CollectionDialogModule,
|
||||||
CollectionAccessRestrictedComponent,
|
VaultComponent,
|
||||||
NoItemsModule,
|
|
||||||
SearchModule,
|
|
||||||
],
|
],
|
||||||
declarations: [VaultComponent, VaultHeaderComponent],
|
|
||||||
exports: [VaultComponent],
|
|
||||||
})
|
})
|
||||||
export class VaultModule {}
|
export class VaultModule {}
|
||||||
|
Loading…
Reference in New Issue
Block a user