mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 01:03:35 +00:00
[PM-2805] Migrate add edit send to Component Library (#6004)
* Converted add-edit send component dialog into a bit-dialog * Updated Send AddEdit text fields to Component Library * Migrated Share and Options fields to ComponentLibrary on SendAddEdit * Migrated footer buttons to ComponentLibrary on SendAddEdit * Updated web's SendAddEdit component file fields * Replaced file upload with component library * Changed SendAddEdit to use Reactive Forms on web * Changed browser SendAddEdit to use ReactiveForms * Update SendAddEdit on desktop to use ReactiveForms * Added AppA11yTitle to button on web SendAddEdit * Initial efflux-dates web change to ComponentLibrary * Corrected delete button to check if it is in EditMode on SendAddEdit * Using BitLink on options button * Corrected typo on send add edit desktop * Replaced efflux-dates with datetime-local input on SendAddEdit web, browser and desktop * Removed efflux dates * Added firefox custom date popout message on DeletionDate to SendAddEdit browser component * moved desktop's new send data reload from send to SendAddEdit component * removing unnecessary attributes and spans from Send AddEdit web * removed redundant try catch from add edit and unnecessary parameter from close * Added type for date select options * Removed unnecessary classes and swapped bootstrap classes by corresponding tailwind classes * Removed unnecessary code * Added file as required field Submit only closes popup on success * Added pre validations at start of submit * PM-3668 removed expiration date from required * PM-3671 not defaulting maximum access count to 0 * PM-3669 Copying the link from link method * Removed required tag from html and added to formgroup * PM-3679 Checking if is not EditMode before validating if FormGroup file value is set * PM-3691 Moved error validation to web component as browser and desktop need to show popup error * PM-3696 - Disabling hide email when it is unset and has policy to not allow hiding * PM-3694 - Properly setting default value for dates on Desktop when changing from an existing send * Disabling hidden required fields * [PM-3800] Clearing password on new send
This commit is contained in:
@@ -53,7 +53,6 @@ import { ExportComponent } from "./tools/export/export.component";
|
||||
import { GeneratorComponent } from "./tools/generator.component";
|
||||
import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component";
|
||||
import { AddEditComponent as SendAddEditComponent } from "./tools/send/add-edit.component";
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from "./tools/send/efflux-dates.component";
|
||||
import { SendComponent } from "./tools/send/send.component";
|
||||
|
||||
@NgModule({
|
||||
@@ -87,7 +86,6 @@ import { SendComponent } from "./tools/send/send.component";
|
||||
SearchComponent,
|
||||
SendAddEditComponent,
|
||||
SendComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SetPasswordComponent,
|
||||
SetPinComponent,
|
||||
SettingsComponent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<form #form [formGroup]="formGroup" (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="send">
|
||||
<div class="box">
|
||||
@@ -16,14 +16,7 @@
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{ "name" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="send.name"
|
||||
appAutofocus
|
||||
[readOnly]="disableSend"
|
||||
/>
|
||||
<input id="name" type="text" name="Name" formControlName="name" appAutofocus />
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-radio" *ngIf="!editMode">
|
||||
<label class="radio-header">{{ "whatTypeOfSend" | i18n }}</label>
|
||||
@@ -31,20 +24,16 @@
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="send.type"
|
||||
name="Type_{{ o.value }}"
|
||||
formControlName="type"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="send.type === o.value"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
<label class="unstyled" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBowRow *ngIf="!editMode && send.type === sendType.File">
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode && type === sendType.File">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<input
|
||||
type="file"
|
||||
@@ -53,22 +42,20 @@
|
||||
name="file"
|
||||
aria-describedby="fileHelp"
|
||||
required
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBowRow *ngIf="editMode && send.type === sendType.File">
|
||||
<div class="box-content-row" appBoxRow *ngIf="editMode && type === sendType.File">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<div class="row-main">{{ send.file.fileName }} ({{ send.file.sizeName }})</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="send.type === sendType.Text">
|
||||
<div class="box-content-row" appBoxRow *ngIf="type === sendType.Text">
|
||||
<label for="text">{{ "text" | i18n }}</label>
|
||||
<textarea
|
||||
id="text"
|
||||
name="text"
|
||||
aria-describedby="textHelp"
|
||||
[(ngModel)]="send.text.text"
|
||||
formControlName="text"
|
||||
rows="6"
|
||||
[readOnly]="disableSend"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,13 +70,7 @@
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideText">{{ "textHiddenByDefault" | i18n }}</label>
|
||||
<input
|
||||
id="hideText"
|
||||
name="hideText"
|
||||
type="checkbox"
|
||||
[(ngModel)]="send.text.hidden"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
<input id="hideText" name="hideText" type="checkbox" formControlName="textHidden" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,14 +93,82 @@
|
||||
</h2>
|
||||
</div>
|
||||
<div [hidden]="!showOptions">
|
||||
<app-send-efflux-dates
|
||||
[initialDeletionDate]="send.deletionDate"
|
||||
[initialExpirationDate]="send.expirationDate"
|
||||
[editMode]="editMode"
|
||||
[disabled]="disableSend"
|
||||
(datesChanged)="setDates($event)"
|
||||
>
|
||||
</app-send-efflux-dates>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
|
||||
<select
|
||||
id="deletionDate"
|
||||
name="DeletionDateSelect"
|
||||
aria-describedby="deletionDateHelp"
|
||||
formControlName="selectedDeletionDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small id="deletionDateHelp" class="help-block">{{
|
||||
"deletionDateDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row"
|
||||
*ngIf="formGroup.controls['selectedDeletionDatePreset'].value === 0 || editMode"
|
||||
>
|
||||
<label *ngIf="editMode" for="deletionDateCustom">{{ "deletionDate" | i18n }}</label>
|
||||
<input
|
||||
id="deletionDateCustom"
|
||||
type="datetime-local"
|
||||
name="deletionDate"
|
||||
aria-describedby="deletionDateCustomHelp"
|
||||
formControlName="defaultDeletionDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
/>
|
||||
<small id="deletionDateCustomHelp" class="help-block" *ngIf="editMode">{{
|
||||
"deletionDateDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="expirationDate">{{ "expirationDate" | i18n }}</label>
|
||||
<select
|
||||
id="expirationDate"
|
||||
name="expirationDateSelect"
|
||||
aria-describedby="expirationDateHelp"
|
||||
formControlName="selectedExpirationDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small id="expirationDateHelp" class="help-block">{{
|
||||
"expirationDateDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row"
|
||||
*ngIf="formGroup.controls['selectedExpirationDatePreset'].value === 0 || editMode"
|
||||
>
|
||||
<label *ngIf="editMode" for="expirationDateCustom">{{
|
||||
"expirationDate" | i18n
|
||||
}}</label>
|
||||
<input
|
||||
id="expirationDateCustom"
|
||||
type="datetime-local"
|
||||
name="expirationDate"
|
||||
aria-describedby="expirationDateCustomHelp"
|
||||
formControlName="defaultExpirationDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
/>
|
||||
<small *ngIf="editMode" id="expirationDateCustomHelp" class="help-block">{{
|
||||
"expirationDateDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
@@ -129,8 +178,7 @@
|
||||
type="number"
|
||||
name="maxAccessCount"
|
||||
aria-describedby="maxAccessCountHelp"
|
||||
[(ngModel)]="send.maxAccessCount"
|
||||
[readOnly]="disableSend"
|
||||
formControlName="maxAccessCount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,8 +202,7 @@
|
||||
name="password"
|
||||
aria-describedby="passwordHelp"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
[(ngModel)]="password"
|
||||
[readOnly]="disableSend"
|
||||
formControlName="password"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
@@ -167,7 +214,6 @@
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
(click)="togglePasswordVisible()"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
@@ -192,9 +238,8 @@
|
||||
id="notes"
|
||||
name="notes"
|
||||
aria-describedby="notesHelp"
|
||||
[(ngModel)]="send.notes"
|
||||
formControlName="notes"
|
||||
rows="6"
|
||||
[readOnly]="disableSend"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -206,13 +251,7 @@
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideEmail">{{ "hideEmail" | i18n }}</label>
|
||||
<input
|
||||
id="hideEmail"
|
||||
type="checkbox"
|
||||
name="HideEmail"
|
||||
[(ngModel)]="send.hideEmail"
|
||||
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend"
|
||||
/>
|
||||
<input id="hideEmail" type="checkbox" name="HideEmail" formControlName="hideEmail" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -220,13 +259,7 @@
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="disabled">{{ "disableSend" | i18n }}</label>
|
||||
<input
|
||||
id="disabled"
|
||||
type="checkbox"
|
||||
name="disabled"
|
||||
[(ngModel)]="send.disabled"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
<input id="disabled" type="checkbox" name="disabled" formControlName="disabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -238,17 +271,11 @@
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
||||
<label for="link">{{ "sendLinkLabel" | i18n }}</label>
|
||||
<input id="link" name="link" [ngModel]="link" readonly />
|
||||
<input id="link" name="link" formControlName="link" readonly />
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="copyLink">{{ "copySendLinkOnSave" | i18n }}</label>
|
||||
<input
|
||||
id="copyLink"
|
||||
name="copyLink"
|
||||
[(ngModel)]="copyLink"
|
||||
type="checkbox"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
<input id="copyLink" name="copyLink" formControlName="copyLink" type="checkbox" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,13 +286,12 @@
|
||||
type="submit"
|
||||
class="primary btn-submit"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading"
|
||||
*ngIf="!disableSend"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span><i class="bwi bwi-save-changes bwi-lg bwi-fw" aria-hidden="true"></i></span>
|
||||
</button>
|
||||
<button type="button" (click)="cancel()" [disabled]="form.loading">
|
||||
<button type="button" (click)="cancel()">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<div class="right">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
@@ -29,7 +30,8 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
policyService: PolicyService,
|
||||
logService: LogService,
|
||||
sendApiService: SendApiService,
|
||||
dialogService: DialogService
|
||||
dialogService: DialogService,
|
||||
formBuilder: FormBuilder
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
@@ -42,7 +44,8 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
logService,
|
||||
stateService,
|
||||
sendApiService,
|
||||
dialogService
|
||||
dialogService,
|
||||
formBuilder
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,6 +53,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
this.password = null;
|
||||
const send = await this.loadSend();
|
||||
this.send = await send.decrypt();
|
||||
this.updateFormValues();
|
||||
this.hasPassword = this.send.password != null && this.send.password.trim() !== "";
|
||||
}
|
||||
|
||||
@@ -65,4 +69,11 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
this.i18nService.t("valueCopied", this.i18nService.t("sendLink"))
|
||||
);
|
||||
}
|
||||
|
||||
async resetAndLoad() {
|
||||
this.sendId = null;
|
||||
this.send = null;
|
||||
await this.load();
|
||||
this.updateFormValues();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<ng-container [formGroup]="datesForm">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
|
||||
<select
|
||||
id="deletionDate"
|
||||
name="DeletionDateSelect"
|
||||
aria-describedby="deletionDateHelp"
|
||||
formControlName="selectedDeletionDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small id="deletionDateHelp" class="help-block">{{ "deletionDateDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedDeletionDatePreset.value === 0 || editMode">
|
||||
<label *ngIf="editMode" for="deletionDateCustom">{{ "deletionDate" | i18n }}</label>
|
||||
<input
|
||||
id="deletionDateCustom"
|
||||
type="datetime-local"
|
||||
name="deletionDate"
|
||||
aria-describedby="deletionDateCustomHelp"
|
||||
formControlName="defaultDeletionDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
/>
|
||||
<small id="deletionDateCustomHelp" class="help-block" *ngIf="editMode">{{
|
||||
"deletionDateDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="expirationDate">{{ "expirationDate" | i18n }}</label>
|
||||
<select
|
||||
id="expirationDate"
|
||||
name="expirationDateSelect"
|
||||
aria-describedby="expirationDateHelp"
|
||||
formControlName="selectedExpirationDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small id="expirationDateHelp" class="help-block">{{ "expirationDateDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0 || editMode">
|
||||
<label *ngIf="editMode" for="expirationDateCustom">{{ "expirationDate" | i18n }}</label>
|
||||
<input
|
||||
id="expirationDateCustom"
|
||||
type="datetime-local"
|
||||
name="expirationDate"
|
||||
aria-describedby="expirationDateCustomHelp"
|
||||
formControlName="defaultExpirationDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
/>
|
||||
<small *ngIf="editMode" id="expirationDateCustomHelp" class="help-block">{{
|
||||
"expirationDateDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -1,38 +0,0 @@
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { Component, OnChanges } from "@angular/core";
|
||||
import { ControlContainer, NgForm } from "@angular/forms";
|
||||
|
||||
import { EffluxDatesComponent as BaseEffluxDatesComponent } from "@bitwarden/angular/tools/send/efflux-dates.component";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-send-efflux-dates",
|
||||
templateUrl: "efflux-dates.component.html",
|
||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
||||
})
|
||||
export class EffluxDatesComponent extends BaseEffluxDatesComponent implements OnChanges {
|
||||
constructor(
|
||||
protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected datePipe: DatePipe
|
||||
) {
|
||||
super(i18nService, platformUtilsService, datePipe);
|
||||
}
|
||||
|
||||
// We reuse the same form on desktop and just swap content, so need to watch these to maintin proper values.
|
||||
ngOnChanges() {
|
||||
this.selectedExpirationDatePreset.setValue(0);
|
||||
this.selectedDeletionDatePreset.setValue(0);
|
||||
this.defaultDeletionDateTime.setValue(
|
||||
this.datePipe.transform(new Date(this.initialDeletionDate), "yyyy-MM-ddTHH:mm")
|
||||
);
|
||||
if (this.initialExpirationDate) {
|
||||
this.defaultExpirationDateTime.setValue(
|
||||
this.datePipe.transform(new Date(this.initialExpirationDate), "yyyy-MM-ddTHH:mm")
|
||||
);
|
||||
} else {
|
||||
this.defaultExpirationDateTime.setValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,12 +91,10 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
|
||||
this.searchBarService.setEnabled(false);
|
||||
}
|
||||
|
||||
addSend() {
|
||||
async addSend() {
|
||||
this.action = Action.Add;
|
||||
if (this.addEditComponent != null) {
|
||||
this.addEditComponent.sendId = null;
|
||||
this.addEditComponent.send = null;
|
||||
this.addEditComponent.load();
|
||||
await this.addEditComponent.resetAndLoad();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user