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:
@@ -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,
|
||||
|
||||
@@ -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">×</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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user