1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +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

@@ -93,7 +93,6 @@ import { GeneratorComponent } from "../tools/generator.component";
import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-history.component";
import { AccessComponent } from "../tools/send/access.component";
import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component";
import { EffluxDatesComponent as SendEffluxDatesComponent } from "../tools/send/efflux-dates.component";
import { ToolsComponent } from "../tools/tools.component";
import { PasswordRepromptComponent } from "../vault/components/password-reprompt.component";
import { PremiumBadgeComponent } from "../vault/components/premium-badge.component";
@@ -198,7 +197,6 @@ import { SharedModule } from "./shared.module";
SecurityKeysComponent,
SelectableAvatarComponent,
SendAddEditComponent,
SendEffluxDatesComponent,
SetPasswordComponent,
SettingsComponent,
ShareComponent,
@@ -302,7 +300,6 @@ import { SharedModule } from "./shared.module";
SecurityKeysComponent,
SelectableAvatarComponent,
SendAddEditComponent,
SendEffluxDatesComponent,
SetPasswordComponent,
SettingsComponent,
ShareComponent,

View File

@@ -1,303 +1,276 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="sendAddEditTitle">
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
<form
class="modal-content"
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
autocomplete="off"
>
<div class="modal-header">
<h1 class="modal-title" id="sendAddEditTitle">{{ title }}</h1>
<button
type="button"
class="close"
data-dismiss="modal"
appA11yTitle="{{ 'close' | i18n }}"
>
<span aria-hidden="true">&times;</span>
</button>
<form
[formGroup]="formGroup"
[bitSubmit]="submitAndClose"
[appApiAction]="formPromise"
autocomplete="off"
>
<bit-dialog dialogSize="large">
<span bitDialogTitle>
{{ title }}
</span>
<span bitDialogContent *ngIf="send">
<bit-callout *ngIf="disableSend">
{{ "sendDisabledWarning" | i18n }}
</bit-callout>
<bit-callout *ngIf="!disableSend && disableHideEmail">
{{ "sendOptionsPolicyInEffect" | i18n }}
<ul class="tw-mb-0">
<li>{{ "sendDisableHideEmailInEffect" | i18n }}</li>
</ul>
</bit-callout>
<bit-form-field class="tw-w-1/2">
<bit-label for="name">{{ "name" | i18n }}</bit-label>
<input bitInput type="text" formControlName="name" />
<bit-hint>{{ "sendNameDesc" | i18n }}</bit-hint>
</bit-form-field>
<div class="tw-flex" *ngIf="!editMode">
<bit-radio-group formControlName="type">
<bit-label>{{ "whatTypeOfSend" | i18n }}</bit-label>
<bit-radio-button
*ngFor="let o of typeOptions"
id="type_{{ o.value }}"
class="tw-block"
[value]="o.value"
>
<bit-label>{{ o.name }}</bit-label>
</bit-radio-button>
</bit-radio-group>
</div>
<div class="modal-body" *ngIf="send">
<app-callout *ngIf="disableSend">
<span>{{ "sendDisabledWarning" | i18n }}</span>
</app-callout>
<app-callout *ngIf="!disableSend && disableHideEmail">
<span>{{ "sendOptionsPolicyInEffect" | i18n }}</span>
<ul class="mb-0">
<li>{{ "sendDisableHideEmailInEffect" | i18n }}</li>
</ul>
</app-callout>
<div class="row">
<div class="col-6 form-group">
<label for="name">{{ "name" | i18n }}</label>
<input
id="name"
class="form-control"
type="text"
name="Name"
[(ngModel)]="send.name"
required
[readOnly]="disableSend"
/>
<small class="form-text text-muted">{{ "sendNameDesc" | i18n }}</small>
</div>
</div>
<div class="row" *ngIf="!editMode">
<div class="col-6 form-group">
<label>{{ "whatTypeOfSend" | i18n }}</label>
<div class="form-check" *ngFor="let o of typeOptions">
<input
class="form-check-input"
type="radio"
[(ngModel)]="send.type"
name="Type_{{ o.value }}"
id="type_{{ o.value }}"
[value]="o.value"
(change)="typeChanged()"
[checked]="send.type === o.value"
/>
<label class="form-check-label" for="type_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
</div>
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<div class="form-group">
<label for="text">{{ "sendTypeText" | i18n }}</label>
<textarea
id="text"
name="Text.Text"
rows="6"
[(ngModel)]="send.text.text"
class="form-control"
[readOnly]="disableSend"
></textarea>
<small class="form-text text-muted">{{ "sendTextDesc" | i18n }}</small>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="send.text.hidden"
id="text-hidden"
name="Text.Hidden"
[disabled]="disableSend"
/>
<label class="form-check-label" for="text-hidden">{{
"textHiddenByDefault" | i18n
}}</label>
</div>
</div>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<div class="form-group">
<div *ngIf="editMode">
<strong class="d-block">{{ "file" | i18n }}</strong>
<!-- Text -->
<ng-container *ngIf="type === sendType.Text">
<bit-form-field>
<bit-label for="text">{{ "sendTypeText" | i18n }}</bit-label>
<textarea bitInput id="text" rows="6" formControlName="text"></textarea>
<bit-hint>{{ "sendTextDesc" | i18n }}</bit-hint>
</bit-form-field>
<bit-form-control>
<input bitCheckbox type="checkbox" formControlName="textHidden" />
<bit-label>{{ "textHiddenByDefault" | i18n }}</bit-label>
</bit-form-control>
</ng-container>
<!-- File -->
<ng-container *ngIf="type === sendType.File">
<div class="tw-flex">
<div *ngIf="editMode">
<bit-label>{{ "file" | i18n }}</bit-label>
<p bitTypography="body1" class="tw-mb-0">
{{ send.file.fileName }} ({{ send.file.sizeName }})
</div>
<div *ngIf="!editMode">
<label for="file">{{ "file" | i18n }}</label>
<input
type="file"
id="file"
class="form-control-file"
name="file"
required
[disabled]="disableSend"
/>
<small class="form-text text-muted"
>{{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }}</small
>
</div>
</p>
</div>
</ng-container>
<h3 class="mt-5">{{ "share" | i18n }}</h3>
<div class="form-group" *ngIf="link">
<label for="link">{{ "sendLinkLabel" | i18n }}</label>
<input type="text" readonly id="link" name="Link" [ngModel]="link" class="form-control" />
</div>
<div class="form-group">
<div class="form-check">
<bit-form-field *ngIf="!editMode">
<bit-label>{{ "file" | i18n }}</bit-label>
<div>
<button bitButton type="button" buttonType="secondary" (click)="fileSelector.click()">
{{ "chooseFile" | i18n }}
</button>
{{ selectedFile?.name ?? ("noFileChosen" | i18n) }}
</div>
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="copyLink"
id="copy-link"
name="CopyLink"
bitInput
#fileSelector
hidden
type="file"
id="file"
name="file"
formControlName="file"
(change)="setSelectedFile($event)"
/>
<label class="form-check-label" for="copy-link">{{
"copySendLinkOnSave" | i18n
}}</label>
</div>
<bit-hint>{{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }}</bit-hint>
</bit-form-field>
</div>
<div
id="options-header"
class="section-header d-flex flex-row align-items-center mt-5"
(click)="toggleOptions()"
>
<h3 class="mb-0 mr-2">
<button type="button" appStopClick class="header-expandable">
<i
class="bwi"
aria-hidden="true"
[ngClass]="{ 'bwi-angle-right': !showOptions, 'bwi-angle-down': showOptions }"
></i>
{{ "options" | i18n }}
</button>
</h3>
</div>
<div id="options" [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="row">
<div class="col-6 form-group">
<label for="maxAccessCount">{{ "maxAccessCount" | i18n }}</label>
<input
id="maxAccessCount"
class="form-control"
type="number"
name="MaxAccessCount"
[(ngModel)]="send.maxAccessCount"
min="1"
[readOnly]="disableSend"
/>
<div class="form-text text-muted small">{{ "maxAccessCountDesc" | i18n }}</div>
</div>
<div class="col-6 form-group" *ngIf="editMode">
<label for="accessCount">{{ "currentAccessCount" | i18n }}</label>
<input
id="accessCount"
class="form-control"
type="text"
name="AccessCount"
readonly
[(ngModel)]="send.accessCount"
/>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<label for="password" *ngIf="!hasPassword">{{ "password" | i18n }}</label>
<label for="password" *ngIf="hasPassword">{{ "newPassword" | i18n }}</label>
<div class="input-group">
<input
id="password"
class="form-control text-monospace"
type="{{ showPassword ? 'text' : 'password' }}"
name="Password"
[(ngModel)]="password"
[readOnly]="disableSend"
/>
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePasswordVisible()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</button>
</div>
</div>
<div class="form-text text-muted small">{{ "sendPasswordDesc" | i18n }}</div>
</div>
</div>
<div class="form-group">
<label for="notes">{{ "notes" | i18n }}</label>
<textarea
id="notes"
name="Notes"
rows="6"
[(ngModel)]="send.notes"
class="form-control"
[readOnly]="disableSend"
></textarea>
<div class="form-text text-muted small">{{ "sendNotesDesc" | i18n }}</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="send.hideEmail"
id="hideEmail"
name="HideEmail"
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend"
/>
<label class="form-check-label" for="hideEmail">
{{ "hideEmail" | i18n }}
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[(ngModel)]="send.disabled"
id="disabled"
name="Disabled"
[disabled]="disableSend"
/>
<label class="form-check-label" for="disabled">{{ "disableThisSend" | i18n }}</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary btn-submit manual"
[ngClass]="{ loading: form.loading }"
[disabled]="form.loading || disableSend"
>
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "save" | i18n }}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
{{ "cancel" | i18n }}
</button>
<div class="ml-auto" *ngIf="send">
<button
#deleteBtn
type="button"
(click)="delete()"
class="btn btn-outline-danger"
appA11yTitle="{{ 'delete' | i18n }}"
*ngIf="editMode"
[disabled]="$any(deleteBtn).loading"
[appApiAction]="deletePromise"
>
</ng-container>
<h4 bitTypography="h4" class="tw-mt-5">{{ "share" | i18n }}</h4>
<bit-form-field *ngIf="link">
<bit-label for="link">{{ "sendLinkLabel" | i18n }}</bit-label>
<input bitInput type="text" readonly formControlName="link" />
</bit-form-field>
<bit-form-control>
<input bitCheckbox type="checkbox" formControlName="copyLink" />
<bit-label>{{ "copySendLinkOnSave" | i18n }}</bit-label>
</bit-form-control>
<div class="tw-mt-5 tw-flex" (click)="toggleOptions()">
<h4 bitTypography="h4" class="tw-mb-0 tw-mr-2">
<button type="button" bitLink appStopClick>
<i
class="bwi bwi-trash bwi-lg bwi-fw"
[hidden]="$any(deleteBtn).loading"
aria-hidden="true"
></i>
<i
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
[hidden]="!$any(deleteBtn).loading"
title="{{ 'loading' | i18n }}"
class="bwi"
aria-hidden="true"
[ngClass]="{ 'bwi-angle-right': !showOptions, 'bwi-angle-down': showOptions }"
></i>
{{ "options" | i18n }}
</button>
</div>
</h4>
</div>
</form>
</div>
</div>
<div id="options" [hidden]="!showOptions">
<div class="tw-flex">
<div *ngIf="!editMode" class="tw-w-1/2 tw-pr-3">
<bit-form-field>
<bit-label for="deletionDate">{{ "deletionDate" | i18n }}</bit-label>
<bit-select
id="deletionDate"
name="SelectedDeletionDatePreset"
formControlName="selectedDeletionDatePreset"
>
<bit-option
*ngFor="let o of deletionDatePresets"
[value]="o.value"
[label]="o.name"
></bit-option>
</bit-select>
<ng-container *ngIf="formGroup.controls['selectedDeletionDatePreset'].value === 0">
<input
bitInput
id="deletionDateCustom"
type="datetime-local"
name="DeletionDate"
formControlName="defaultDeletionDateTime"
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
</ng-container>
<bit-hint>{{ "deletionDateDesc" | i18n }}</bit-hint>
</bit-form-field>
</div>
<div *ngIf="editMode" class="tw-w-1/2 tw-pr-3">
<bit-form-field>
<bit-label for="deletionDate">{{ "deletionDate" | i18n }}</bit-label>
<input
bitInput
id="deletionDateCustom"
type="datetime-local"
name="DeletionDate"
formControlName="defaultDeletionDateTime"
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
<bit-hint>{{ "deletionDateDesc" | i18n }}</bit-hint>
</bit-form-field>
</div>
<div *ngIf="!editMode" class="tw-w-1/2 tw-pl-3">
<bit-form-field>
<bit-label for="expirationDate">
{{ "expirationDate" | i18n }}
</bit-label>
<bit-select
bitInput
id="expirationDate"
name="SelectedExpirationDatePreset"
formControlName="selectedExpirationDatePreset"
>
<bit-option
*ngFor="let e of expirationDatePresets"
[value]="e.value"
[label]="e.name"
></bit-option>
</bit-select>
<ng-container *ngIf="formGroup.controls['selectedExpirationDatePreset'].value === 0">
<input
bitInput
id="expirationDateCustom"
type="datetime-local"
name="ExpirationDate"
formControlName="defaultExpirationDateTime"
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
</ng-container>
<bit-hint>{{ "expirationDateDesc" | i18n }}</bit-hint>
</bit-form-field>
</div>
<div *ngIf="editMode" class="tw-w-1/2 tw-pl-3">
<bit-form-field>
<bit-label class="tw-flex" for="expirationDate">
{{ "expirationDate" | i18n }}
<button
type="button"
bitLink
appStopClick
(click)="clearExpiration()"
*ngIf="!disableSend"
class="tw-ml-auto"
>
{{ "clear" | i18n }}
</button>
</bit-label>
<input
bitInput
id="expirationDateCustom"
type="datetime-local"
name="ExpirationDate"
formControlName="defaultExpirationDateTime"
placeholder="MM/DD/YYYY HH:MM AM/PM"
/>
<bit-hint>{{ "expirationDateDesc" | i18n }}</bit-hint>
</bit-form-field>
</div>
</div>
<div class="tw-flex">
<bit-form-field class="tw-w-1/2 tw-pr-3">
<bit-label for="maxAccessCount">{{ "maxAccessCount" | i18n }}</bit-label>
<input bitInput type="number" formControlName="maxAccessCount" min="1" />
<bit-hint>{{ "maxAccessCountDesc" | i18n }}</bit-hint>
</bit-form-field>
<bit-form-field class="tw-w-1/2 tw-pl-3" *ngIf="editMode">
<bit-label for="accessCount">{{ "currentAccessCount" | i18n }}</bit-label>
<input bitInput type="text" formControlName="accessCount" readonly />
</bit-form-field>
</div>
<div class="tw-flex">
<bit-form-field class="tw-w-1/2 tw-pr-3">
<bit-label for="password" *ngIf="!hasPassword">{{ "password" | i18n }}</bit-label>
<bit-label for="password" *ngIf="hasPassword">{{ "newPassword" | i18n }}</bit-label>
<input bitInput type="password" formControlName="password" />
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
<bit-hint>{{ "sendPasswordDesc" | i18n }}</bit-hint>
</bit-form-field>
</div>
<bit-form-field>
<bit-label>{{ "notes" | i18n }}</bit-label>
<textarea bitInput formControlName="notes" rows="6"></textarea>
<bit-hint>{{ "sendNotesDesc" | i18n }}</bit-hint>
</bit-form-field>
<bit-form-control>
<input bitCheckbox type="checkbox" formControlName="hideEmail" />
<bit-label>{{ "hideEmail" | i18n }}</bit-label>
</bit-form-control>
<bit-form-control>
<input bitCheckbox type="checkbox" formControlName="disabled" />
<bit-label>{{ "disableThisSend" | i18n }}</bit-label>
</bit-form-control>
</div>
</span>
<ng-container bitDialogFooter>
<button
type="submit"
bitButton
bitFormButton
[appA11yTitle]="'save' | i18n"
buttonType="primary"
>
{{ "save" | i18n }}
</button>
<button
type="button"
bitButton
buttonType="secondary"
[appA11yTitle]="'cancel' | i18n"
bitDialogClose
>
{{ "cancel" | i18n }}
</button>
<button
*ngIf="editMode"
type="button"
class="tw-ml-auto"
bitIconButton="bwi-trash"
buttonType="danger"
[appA11yTitle]="'delete' | i18n"
[bitAction]="deleteAndClose"
></button>
</ng-container>
</bit-dialog>
</form>

View File

@@ -1,5 +1,7 @@
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { DatePipe } from "@angular/common";
import { Component } from "@angular/core";
import { Component, Inject } 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";
@@ -19,6 +21,7 @@ import { DialogService } from "@bitwarden/components";
})
export class AddEditComponent extends BaseAddEditComponent {
override componentName = "app-send-add-edit";
protected selectedFile: File;
constructor(
i18nService: I18nService,
@@ -31,7 +34,10 @@ export class AddEditComponent extends BaseAddEditComponent {
policyService: PolicyService,
logService: LogService,
sendApiService: SendApiService,
dialogService: DialogService
dialogService: DialogService,
formBuilder: FormBuilder,
protected dialogRef: DialogRef,
@Inject(DIALOG_DATA) params: { sendId: string }
) {
super(
i18nService,
@@ -44,8 +50,11 @@ export class AddEditComponent extends BaseAddEditComponent {
logService,
stateService,
sendApiService,
dialogService
dialogService,
formBuilder
);
this.sendId = params.sendId;
}
async copyLinkToClipboard(link: string): Promise<void | boolean> {
@@ -55,4 +64,29 @@ export class AddEditComponent extends BaseAddEditComponent {
window.setTimeout(() => resolve(super.copyLinkToClipboard(link)), 500);
});
}
protected setSelectedFile(event: Event) {
const fileInputEl = <HTMLInputElement>event.target;
const file = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null;
this.selectedFile = file;
}
submitAndClose = async () => {
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
return;
}
const success = await this.submit();
if (success) {
this.dialogRef.close();
}
};
deleteAndClose = async () => {
const success = await this.delete();
if (success) {
this.dialogRef.close();
}
};
}

View File

@@ -1,188 +0,0 @@
<div class="row" [formGroup]="datesForm">
<div class="col-6 form-group">
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
<ng-template #deletionDateCustom>
<ng-container [ngSwitch]="browserPath">
<ng-container *ngSwitchCase="'firefox'">
<div class="d-flex justify-content-around">
<input
id="deletionDateCustomFallback"
class="form-control mt-1"
type="date"
name="DeletionDateFallback"
formControlName="fallbackDeletionDate"
required
placeholder="MM/DD/YYYY"
data-date-format="mm/dd/yyyy"
/>
<input
id="deletionTimeCustomFallback"
class="form-control mt-1 ml-1"
type="time"
name="DeletionTimeDate"
formControlName="fallbackDeletionTime"
required
placeholder="HH:MM AM/PM"
/>
</div>
</ng-container>
<ng-container *ngSwitchCase="'safari'">
<div class="d-flex justify-content-around">
<input
id="deletionDateCustomFallback"
class="form-control mt-1"
type="date"
name="DeletionDateFallback"
formControlName="fallbackDeletionDate"
required
placeholder="MM/DD/YYYY"
data-date-format="mm/dd/yyyy"
/>
<select
id="deletionTimeCustomFallback"
class="form-control mt-1 ml-1"
[required]="!editMode"
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"
class="form-control mt-1"
type="datetime-local"
name="DeletionDate"
formControlName="defaultDeletionDateTime"
required
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disabled"
/>
</ng-container>
</ng-container>
</ng-template>
<div *ngIf="!editMode">
<select
id="deletionDate"
name="SelectedDeletionDatePreset"
formControlName="selectedDeletionDatePreset"
class="form-control"
required
>
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{ o.name }}</option>
</select>
<ng-container *ngIf="selectedDeletionDatePreset.value === 0">
<ng-container *ngTemplateOutlet="deletionDateCustom"> </ng-container>
</ng-container>
</div>
<div *ngIf="editMode">
<ng-container *ngTemplateOutlet="deletionDateCustom"> </ng-container>
</div>
<div class="form-text text-muted small">{{ "deletionDateDesc" | i18n }}</div>
</div>
<div class="col-6 form-group">
<div class="d-flex">
<label for="expirationDate">{{ "expirationDate" | i18n }}</label>
<a
href="#"
appStopClick
(click)="clearExpiration()"
class="ml-auto"
*ngIf="editMode && !disabled"
>
{{ "clear" | i18n }}
</a>
</div>
<ng-template #expirationDateCustom>
<ng-container [ngSwitch]="browserPath">
<div *ngSwitchCase="'firefox'" class="d-flex justify-content-around">
<input
id="expirationDateCustomFallback"
class="form-control mt-1"
type="date"
name="ExpirationDateFallback"
formControlName="fallbackExpirationDate"
[required]="!editMode"
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<input
id="expirationTimeCustomFallback"
class="form-control mt-1 ml-1"
type="time"
name="ExpirationTimeFallback"
formControlName="fallbackExpirationTime"
[required]="!editMode"
placeholder="HH:MM AM/PM"
[readOnly]="disabled"
/>
</div>
<!-- non-default cases are not showing up -->
<div *ngSwitchCase="'safari'" class="d-flex justify-content-around">
<input
id="expirationDateCustomFallback"
class="form-control mt-1"
type="date"
name="ExpirationDateFallback"
formControlName="fallbackExpirationDate"
[required]="!editMode"
placeholder="MM/DD/YYYY"
[readOnly]="disabled"
data-date-format="mm/dd/yyyy"
/>
<select
id="expirationTimeCustomFallback"
class="form-control mt-1 ml-1"
[required]="!editMode"
formControlName="fallbackExpirationTime"
name="SafariExpirationTime"
>
<option
*ngFor="let o of safariExpirationTimePresetOptions"
[ngValue]="o.twentyFourHour"
>
{{ o.twelveHour }}
</option>
</select>
</div>
<ng-container *ngSwitchDefault>
<input
id="expirationDateCustom"
class="form-control mt-1"
type="datetime-local"
name="ExpirationDate"
formControlName="defaultExpirationDateTime"
placeholder="MM/DD/YYYY HH:MM AM/PM"
[readOnly]="disabled"
/>
</ng-container>
</ng-container>
</ng-template>
<div *ngIf="!editMode">
<select
id="expirationDate"
name="SelectedExpirationDatePreset"
formControlName="selectedExpirationDatePreset"
class="form-control"
required
>
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{ o.name }}</option>
</select>
<ng-container *ngIf="selectedExpirationDatePreset.value === 0">
<ng-container *ngTemplateOutlet="expirationDateCustom"> </ng-container>
</ng-container>
</div>
<div *ngIf="editMode">
<ng-container *ngTemplateOutlet="expirationDateCustom"> </ng-container>
</div>
<div class="form-text text-muted small">{{ "expirationDateDesc" | i18n }}</div>
</div>
</div>

View File

@@ -1,22 +0,0 @@
import { DatePipe } from "@angular/common";
import { Component } 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 {
constructor(
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected datePipe: DatePipe
) {
super(i18nService, platformUtilsService, datePipe);
}
}

View File

@@ -1,4 +1,5 @@
import { Component, NgZone, ViewChild, ViewContainerRef } from "@angular/core";
import { lastValueFrom } from "rxjs";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component";
@@ -98,29 +99,17 @@ export class SendComponent extends BaseSendComponent {
return;
}
const component = await this.editSend(null);
component.type = this.type;
await this.editSend(null);
}
async editSend(send: SendView) {
const [modal, childComponent] = await this.modalService.openViewRef(
AddEditComponent,
this.sendAddEditModalRef,
(comp) => {
comp.sendId = send == null ? null : send.id;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
comp.onSavedSend.subscribe(async () => {
modal.close();
await this.load();
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
comp.onDeletedSend.subscribe(async () => {
modal.close();
await this.load();
});
}
);
const dialog = this.dialogService.open(AddEditComponent, {
data: {
sendId: send == null ? null : send.id,
},
});
return childComponent;
await lastValueFrom(dialog.closed);
await this.load();
}
}