1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00
Files
browser/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts
Nick Krantz aa024b419c [PM-12571][PM-13807] Add/Edit Folder Dialog (#12487)
* move `add-edit-folder` component to `angular/vault/components` so it can be consumed by other platforms

* add edit/add folder copy to web app copy

* add extension refresh folder dialog to individual vault

* adding folder delete message to the web

* add deletion result for add/edit folder dialog

* allow editing folder from web

* fix strict types for changed files

* update tests

* remove border class so hover state shows

* revert changes to new-item-dropdown-v2

* migrate `AddEditFolderDialogComponent` to `libs/vault` package

* add Created enum type

* add static open method for folder dialog

* add fullName to `FolderFilter` type

* save the full name of a folder before splitting it into parts

* use the full name of the folder filter when available

* use a shallow copy to edit the folder's full name

---------

Co-authored-by: SmithThe4th <gsmith@bitwarden.com>
2025-02-04 15:13:13 -06:00

176 lines
5.2 KiB
TypeScript

import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { CommonModule } from "@angular/common";
import {
AfterViewInit,
Component,
DestroyRef,
inject,
Inject,
OnInit,
ViewChild,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { firstValueFrom, map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import {
AsyncActionsModule,
BitSubmitDirective,
ButtonComponent,
ButtonModule,
DialogModule,
DialogService,
FormFieldModule,
IconButtonModule,
ToastService,
} from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
export enum AddEditFolderDialogResult {
Created = "created",
Deleted = "deleted",
}
export type AddEditFolderDialogData = {
/** When provided, dialog will display edit folder variant */
editFolderConfig?: { folder: FolderView };
};
@Component({
standalone: true,
selector: "vault-add-edit-folder-dialog",
templateUrl: "./add-edit-folder-dialog.component.html",
imports: [
CommonModule,
JslibModule,
DialogModule,
ButtonModule,
FormFieldModule,
ReactiveFormsModule,
IconButtonModule,
AsyncActionsModule,
],
})
export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
@ViewChild(BitSubmitDirective) private bitSubmit?: BitSubmitDirective;
@ViewChild("submitBtn") private submitBtn?: ButtonComponent;
folder: FolderView = new FolderView();
variant: "add" | "edit" = "add";
folderForm = this.formBuilder.group({
name: ["", Validators.required],
});
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
private destroyRef = inject(DestroyRef);
constructor(
private formBuilder: FormBuilder,
private folderService: FolderService,
private folderApiService: FolderApiServiceAbstraction,
private accountService: AccountService,
private keyService: KeyService,
private toastService: ToastService,
private i18nService: I18nService,
private logService: LogService,
private dialogService: DialogService,
private dialogRef: DialogRef<AddEditFolderDialogResult>,
@Inject(DIALOG_DATA) private data?: AddEditFolderDialogData,
) {}
ngOnInit(): void {
if (this.data?.editFolderConfig) {
this.variant = "edit";
this.folderForm.controls.name.setValue(this.data.editFolderConfig.folder.name);
this.folder = this.data.editFolderConfig.folder;
} else {
// Create a new folder view
this.folder = new FolderView();
}
}
ngAfterViewInit(): void {
this.bitSubmit?.loading$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((loading) => {
if (!this.submitBtn) {
return;
}
this.submitBtn.loading = loading;
});
}
/** Submit the new folder */
submit = async () => {
if (this.folderForm.invalid) {
return;
}
this.folder.name = this.folderForm.controls.name.value ?? "";
try {
const activeUserId = await firstValueFrom(this.activeUserId$);
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId!);
const folder = await this.folderService.encrypt(this.folder, userKey);
await this.folderApiService.save(folder, activeUserId!);
this.toastService.showToast({
variant: "success",
title: "",
message: this.i18nService.t("editedFolder"),
});
this.close(AddEditFolderDialogResult.Created);
} catch (e) {
this.logService.error(e);
}
};
/** Delete the folder with when the user provides a confirmation */
deleteFolder = async () => {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "deleteFolder" },
content: { key: "deleteFolderPermanently" },
type: "warning",
});
if (!confirmed) {
return;
}
try {
const activeUserId = await firstValueFrom(this.activeUserId$);
await this.folderApiService.delete(this.folder.id, activeUserId!);
this.toastService.showToast({
variant: "success",
title: "",
message: this.i18nService.t("deletedFolder"),
});
} catch (e) {
this.logService.error(e);
}
this.close(AddEditFolderDialogResult.Deleted);
};
/** Close the dialog */
private close(result: AddEditFolderDialogResult) {
this.dialogRef.close(result);
}
static open(dialogService: DialogService, data?: AddEditFolderDialogData) {
return dialogService.open<AddEditFolderDialogResult, AddEditFolderDialogData>(
AddEditFolderDialogComponent,
{ data },
);
}
}