1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 00:03:56 +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:
aj-rosado
2023-09-07 13:49:13 +01:00
committed by GitHub
parent 86bdfaa7ba
commit 5f78aeaef2
20 changed files with 777 additions and 1388 deletions

View File

@@ -33,7 +33,6 @@ import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.
import { GeneratorComponent } from "../tools/popup/generator/generator.component";
import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component";
import { SendListComponent } from "../tools/popup/send/components/send-list.component";
import { EffluxDatesComponent as SendEffluxDatesComponent } from "../tools/popup/send/efflux-dates.component";
import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component";
import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component";
import { SendTypeComponent } from "../tools/popup/send/send-type.component";
@@ -133,7 +132,6 @@ import "../platform/popup/locales";
PrivateModeWarningComponent,
RegisterComponent,
SendAddEditComponent,
SendEffluxDatesComponent,
SendGroupingsComponent,
SendListComponent,
SendTypeComponent,

View File

@@ -1,217 +0,0 @@
<ng-container [formGroup]="datesForm">
<div class="box">
<div class="box-content">
<ng-container *ngIf="!editMode">
<div class="box-content-row" appBoxRow>
<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>
</div>
<div class="box-content-row" appBoxRow *ngIf="selectedDeletionDatePreset.value === 0">
<ng-container *ngTemplateOutlet="deletionDateCustom"></ng-container>
</div>
</ng-container>
<div class="box-content-row" appBoxRow *ngIf="editMode">
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
<ng-container *ngTemplateOutlet="deletionDateCustom"></ng-container>
</div>
</div>
<div id="deletionDateHelp" class="box-footer">
{{ "deletionDateDesc" | i18n }}
<ng-container
*ngIf="
!inPopout &&
browserPath == 'firefox' &&
(editMode || (selectedDeletionDatePreset.value === 0 && !editMode))
"
>
<br />{{ "sendFirefoxCustomDatePopoutMessage1" | i18n }}
<a (click)="popOutWindow.emit()">{{ "sendFirefoxCustomDatePopoutMessage2" | i18n }}</a>
{{ "sendFirefoxCustomDatePopoutMessage3" | i18n }}
</ng-container>
</div>
</div>
<div class="box">
<div class="box-content">
<ng-container *ngIf="!editMode">
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
<label for="editExpirationDate">{{ "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>
</div>
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0" appBoxRow>
<ng-container *ngTemplateOutlet="expirationDateCustom"></ng-container>
</div>
</ng-container>
<div class="box-content-row" *ngIf="editMode" appBoxRow>
<div class="flex-label">
<label>{{ "expirationDate" | i18n }}</label>
<button type="button" *ngIf="!disabled" appStopClick (click)="clearExpiration()">
{{ "clear" | i18n }}
</button>
</div>
<ng-container *ngTemplateOutlet="expirationDateCustom"></ng-container>
</div>
</div>
<div id="expirationDateHelp" class="box-footer">
{{ "expirationDateDesc" | i18n }}
<ng-container
*ngIf="
!inPopout &&
browserPath == 'firefox' &&
(editMode || (selectedExpirationDatePreset.value === 0 && !editMode))
"
>
<br />{{ "sendFirefoxCustomDatePopoutMessage1" | i18n }}
<a (click)="popOutWindow.emit()">{{ "sendFirefoxCustomDatePopoutMessage2" | i18n }}</a>
{{ "sendFirefoxCustomDatePopoutMessage3" | i18n }}
</ng-container>
</div>
</div>
<ng-template #deletionDateCustom>
<ng-container [ngSwitch]="browserPath">
<ng-container *ngSwitchCase="'firefox'">
<div class="flex flex-grow">
<input
id="deletionDateCustomFallback"
type="date"
name="DeletionDateFallback"
aria-describedby="deletionDateHelp"
formControlName="fallbackDeletionDate"
required
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<input
id="deletionTimeCustomFallback"
type="time"
name="DeletionTimeDate"
formControlName="fallbackDeletionTime"
required
placeholder="HH:MM AM/PM"
[readOnly]="disabled"
/>
</div>
</ng-container>
<ng-container *ngSwitchCase="'safari'">
<div class="flex flex-grow">
<input
id="deletionDateCustomFallback"
type="date"
name="DeletionDateFallback"
aria-describedby="deletionDateHelp"
formControlName="fallbackDeletionDate"
required
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<select
id="deletionTimeCustomFallback"
formControlName="fallbackDeletionTime"
name="SafariDeletionTime"
>
<option *ngFor="let o of safariDeletionTimePresetOptions" [ngValue]="o.twentyFourHour">
{{ o.twelveHour }}
</option>
</select>
</div>
</ng-container>
<ng-container *ngSwitchDefault>
<input
id="deletionDateCustom"
type="datetime-local"
name="DeletionDate"
aria-describedby="deletionDateHelp"
formControlName="defaultDeletionDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
</ng-container>
</ng-container>
</ng-template>
<ng-template #expirationDateCustom>
<ng-container [ngSwitch]="browserPath">
<ng-container *ngSwitchCase="'firefox'">
<div class="flex flex-grow">
<input
id="expirationDateCustomFallback"
type="date"
name="ExpirationDateFallback"
aria-describedby="expirationDateHelp"
formControlName="fallbackExpirationDate"
[required]="!editMode"
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<input
id="expirationTimeCustomFallback"
type="time"
name="ExpirationTimeFallback"
formControlName="fallbackExpirationTime"
[required]="!editMode"
placeholder="HH:MM AM/PM"
[readOnly]="disabled"
/>
</div>
</ng-container>
<ng-container *ngSwitchCase="'safari'">
<div class="flex flex-grow">
<input
id="expirationDateCustomFallback"
type="date"
name="ExpirationDateFallback"
aria-describedby="expirationDateHelp"
formControlName="fallbackExpirationDate"
[required]="!editMode"
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<select
id="expirationTimeCustomFallback"
formControlName="fallbackExpirationTime"
name="SafariExpirationTime"
>
<option
*ngFor="let o of safariExpirationTimePresetOptions"
[ngValue]="o.twentyFourHour"
>
{{ o.twelveHour }}
</option>
</select>
</div>
</ng-container>
<ng-container *ngSwitchDefault>
<input
id="expirationDateCustom"
type="datetime-local"
name="ExpirationDate"
aria-describedby="expirationDateHelp"
formControlName="defaultExpirationDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disabled"
/>
</ng-container>
</ng-container>
</ng-template>
</ng-container>

View File

@@ -1,25 +0,0 @@
import { DatePipe } from "@angular/common";
import { Component, EventEmitter, Input, Output } 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 {
@Input() readonly inPopout: boolean;
@Output() popOutWindow = new EventEmitter();
constructor(
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected datePipe: DatePipe
) {
super(i18nService, platformUtilsService, datePipe);
}
}

View File

@@ -1,4 +1,4 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" [formGroup]="formGroup">
<header>
<div class="left">
<button type="button" (click)="cancel()">{{ "cancel" | i18n }}</button>
@@ -7,7 +7,7 @@
<span class="title">{{ title }}</span>
</h1>
<div class="right">
<button type="submit" [disabled]="form.loading || disableSend">
<button type="submit">
<span [hidden]="form.loading">{{ "save" | i18n }}</span>
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
@@ -42,9 +42,8 @@
<input
id="name"
type="text"
name="Name"
formControlName="name"
aria-describedby="nameHelp"
[(ngModel)]="send.name"
[readonly]="disableSend"
/>
</div>
@@ -66,12 +65,9 @@
>
<input
type="radio"
[(ngModel)]="send.type"
name="Type_{{ o.value }}"
formControlName="type"
id="type_{{ o.value }}"
[value]="o.value"
(change)="typeChanged()"
[checked]="send.type === o.value"
[readonly]="disableSend"
/>
<label for="type_{{ o.value }}">
@@ -82,7 +78,7 @@
</div>
</div>
<!-- File -->
<div class="box" *ngIf="send.type === sendType.File && (editMode || showFileSelector)">
<div class="box" *ngIf="type === sendType.File && (editMode || showFileSelector)">
<div class="box-content no-hover">
<div class="box-content-row" *ngIf="editMode">
<label for="file">{{ "file" | i18n }}</label>
@@ -93,9 +89,8 @@
<input
type="file"
id="file"
name="file"
formControlName="file"
aria-describedby="fileHelp"
required
[readonly]="disableSend"
/>
</div>
@@ -105,17 +100,15 @@
</div>
</div>
<!-- Text -->
<div class="box" *ngIf="send.type === sendType.Text">
<div class="box" *ngIf="type === sendType.Text">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="text">{{ "sendTypeText" | i18n }}</label>
<textarea
id="text"
name="Text"
formControlName="text"
aria-describedby="textHelp"
rows="6"
[(ngModel)]="send.text.text"
[readonly]="disableSend"
></textarea>
</div>
</div>
@@ -125,13 +118,7 @@
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="hideText">{{ "sendHideText" | i18n }}</label>
<input
id="hideText"
type="checkbox"
name="HideText"
[(ngModel)]="send.text.hidden"
[disabled]="disableSend"
/>
<input id="hideText" type="checkbox" name="HideText" formControlName="textHidden" />
</div>
</div>
</div>
@@ -144,13 +131,7 @@
<!-- Copy Link on Save -->
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="copyOnSave">{{ "sendShareDesc" | i18n }}</label>
<input
id="copyOnSave"
type="checkbox"
name="CopyOnSave"
[(ngModel)]="copyLink"
[disabled]="disableSend"
/>
<input id="copyOnSave" type="checkbox" name="CopyOnSave" formControlName="copyLink" />
</div>
</div>
</div>
@@ -170,15 +151,140 @@
</h2>
</div>
<div [hidden]="!showOptions">
<app-send-efflux-dates
[initialDeletionDate]="send.deletionDate"
[initialExpirationDate]="send.expirationDate"
[editMode]="editMode"
[disabled]="disableSend"
(datesChanged)="setDates($event)"
(popOutWindow)="popOutWindow()"
>
</app-send-efflux-dates>
<!-- Deletion Date -->
<div class="box">
<div class="box-content">
<ng-container *ngIf="!editMode">
<div class="box-content-row" appBoxRow>
<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>
</div>
<div
class="box-content-row"
appBoxRow
*ngIf="formGroup.controls['selectedDeletionDatePreset'].value === 0"
>
<input
id="deletionDateCustom"
type="datetime-local"
name="DeletionDate"
aria-describedby="deletionDateHelp"
formControlName="defaultDeletionDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
</div>
</ng-container>
<div class="box-content-row" appBoxRow *ngIf="editMode">
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
<input
id="deletionDateCustom"
type="datetime-local"
name="DeletionDate"
aria-describedby="deletionDateHelp"
formControlName="defaultDeletionDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
</div>
</div>
<div id="deletionDateHelp" class="box-footer">
{{ "deletionDateDesc" | i18n }}
<ng-container
*ngIf="
!inPopout &&
isFirefox &&
(editMode ||
(formGroup.controls['selectedDeletionDatePreset'].value === 0 && !editMode))
"
>
<br />{{ "sendFirefoxCustomDatePopoutMessage1" | i18n }}
<a (click)="popOutWindow()">{{ "sendFirefoxCustomDatePopoutMessage2" | i18n }}</a>
{{ "sendFirefoxCustomDatePopoutMessage3" | i18n }}
</ng-container>
</div>
</div>
<!-- Expiration Date -->
<div class="box">
<div class="box-content">
<ng-container *ngIf="!editMode">
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
<label for="editExpirationDate">{{ "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>
</div>
<div
class="box-content-row"
*ngIf="formGroup.controls['selectedExpirationDatePreset'].value === 0"
appBoxRow
>
<input
id="expirationDateCustom"
type="datetime-local"
name="ExpirationDate"
aria-describedby="expirationDateHelp"
formControlName="defaultExpirationDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disableSend"
/>
</div>
</ng-container>
<div class="box-content-row" *ngIf="editMode" appBoxRow>
<div class="flex-label">
<label>{{ "expirationDate" | i18n }}</label>
<button type="button" *ngIf="!disableSend" appStopClick (click)="clearExpiration()">
{{ "clear" | i18n }}
</button>
</div>
<input
id="expirationDateCustom"
type="datetime-local"
name="ExpirationDate"
aria-describedby="expirationDateHelp"
formControlName="defaultExpirationDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disableSend"
/>
</div>
</div>
<div id="expirationDateHelp" class="box-footer">
{{ "expirationDateDesc" | i18n }}
<ng-container
*ngIf="
!inPopout &&
isFirefox &&
(editMode ||
(formGroup.controls['selectedExpirationDatePreset'].value === 0 && !editMode))
"
>
<br />{{ "sendFirefoxCustomDatePopoutMessage1" | i18n }}
<a (click)="popOutWindow()">{{ "sendFirefoxCustomDatePopoutMessage2" | i18n }}</a>
{{ "sendFirefoxCustomDatePopoutMessage3" | i18n }}
</ng-container>
</div>
</div>
<!-- Maximum Access Count -->
<div class="box">
<div class="box-content">
@@ -190,8 +296,7 @@
type="number"
name="MaximumAccessCount"
aria-describedby="maximumAccessCountHelp"
[(ngModel)]="send.maxAccessCount"
[readonly]="disableSend"
formControlName="maxAccessCount"
/>
</div>
</div>
@@ -206,10 +311,9 @@
<label for="currentAccessCount">{{ "currentAccessCount" | i18n }}</label>
<input
id="currentAccessCount"
readonly
type="text"
name="CurrentAccessCount"
[(ngModel)]="send.accessCount"
formControlName="accessCount"
/>
</div>
</div>
@@ -227,9 +331,8 @@
name="Password"
aria-describedby="passwordHelp"
class="monospaced"
[(ngModel)]="password"
formControlName="password"
appInputVerbatim
[readonly]="disableSend"
/>
</div>
<div class="action-buttons" *ngIf="!disableSend">
@@ -264,8 +367,7 @@
name="Notes"
aria-describedby="notesHelp"
rows="6"
[(ngModel)]="send.notes"
[readonly]="disableSend"
formControlName="notes"
></textarea>
</div>
</div>
@@ -278,13 +380,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>
@@ -293,13 +389,7 @@
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="disableSend">{{ "sendDisableDesc" | i18n }}</label>
<input
id="disableSend"
type="checkbox"
name="DisableSend"
[(ngModel)]="send.disabled"
[disabled]="disableSend"
/>
<input id="disableSend" type="checkbox" name="DisableSend" formControlName="disabled" />
</div>
</div>
</div>

View File

@@ -1,5 +1,6 @@
import { DatePipe, Location } from "@angular/common";
import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
@@ -47,7 +48,8 @@ export class SendAddEditComponent extends BaseAddEditComponent {
private popupUtilsService: PopupUtilsService,
logService: LogService,
sendApiService: SendApiService,
dialogService: DialogService
dialogService: DialogService,
formBuilder: FormBuilder
) {
super(
i18nService,
@@ -60,7 +62,8 @@ export class SendAddEditComponent extends BaseAddEditComponent {
logService,
stateService,
sendApiService,
dialogService
dialogService,
formBuilder
);
}