1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-11 10:10:25 +01:00

[PM-11904] - send options form (#11142)

* send options form

* remove commented code

* fix margin. update i18 key

* remove unecessary input

* remove unnecessary typing. DRY up messages
This commit is contained in:
Jordan Aasen 2024-09-19 11:53:25 -07:00 committed by GitHub
parent eec84d893d
commit c4d66a1383
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 180 additions and 72 deletions

View File

@ -531,6 +531,9 @@
"notes": { "notes": {
"message": "Notes" "message": "Notes"
}, },
"privateNote": {
"message": "Private note"
},
"note": { "note": {
"message": "Note" "message": "Note"
}, },
@ -2267,6 +2270,23 @@
"excludedDomainsSavedSuccess": { "excludedDomainsSavedSuccess": {
"message": "Excluded domain changes saved" "message": "Excluded domain changes saved"
}, },
"limitSendViews": {
"message": "Limit views"
},
"limitSendViewsHint": {
"message": "No one can view this Send after the limit is reached.",
"description": "Displayed under the limit views field on Send"
},
"limitSendViewsCount": {
"message": "$ACCESSCOUNT$ views left",
"description": "Displayed under the limit views field on Send",
"placeholders": {
"accessCount": {
"content": "$1",
"example": "2"
}
}
},
"send": { "send": {
"message": "Send", "message": "Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@ -2409,6 +2429,10 @@
"message": "Optionally require a password for users to access this Send.", "message": "Optionally require a password for users to access this Send.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
}, },
"sendPasswordDescV2": {
"message": "Require this password to view the Send.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendNotesDesc": { "sendNotesDesc": {
"message": "Private notes about this Send.", "message": "Private notes about this Send.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
@ -2514,6 +2538,9 @@
"hideEmail": { "hideEmail": {
"message": "Hide my email address from recipients." "message": "Hide my email address from recipients."
}, },
"hideYourEmail": {
"message": "Hide your email address from viewers."
},
"sendOptionsPolicyInEffect": { "sendOptionsPolicyInEffect": {
"message": "One or more organization policies are affecting your Send options." "message": "One or more organization policies are affecting your Send options."
}, },

View File

@ -0,0 +1,31 @@
<bit-section [formGroup]="sendOptionsForm">
<bit-section-header>
<h2 class="tw-mt-4" bitTypography="h5">{{ "additionalOptions" | i18n }}</h2>
</bit-section-header>
<bit-card>
<bit-form-field>
<bit-label>{{ "limitSendViews" | i18n }}</bit-label>
<input bitInput type="number" formControlName="maxAccessCount" min="1" />
<bit-hint>{{ "limitSendViewsHint" | i18n }}</bit-hint>
<bit-hint *ngIf="shouldShowCount"
>&nbsp;{{ "limitSendViewsCount" | i18n: viewsLeft }}</bit-hint
>
</bit-form-field>
<bit-form-field>
<bit-label *ngIf="!hasPassword">{{ "password" | i18n }}</bit-label>
<bit-label *ngIf="hasPassword">{{ "newPassword" | i18n }}</bit-label>
<input bitInput type="password" formControlName="password" />
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
<button type="button" bitIconButton="bwi-refresh" bitSuffix></button>
<bit-hint>{{ "sendPasswordDescV2" | i18n }}</bit-hint>
</bit-form-field>
<bit-form-control>
<input bitCheckbox type="checkbox" formControlName="hideEmail" />
<bit-label>{{ "hideYourEmail" | i18n }}</bit-label>
</bit-form-control>
<bit-form-field>
<bit-label>{{ "privateNote" | i18n }}</bit-label>
<textarea bitInput rows="4" formControlName="notes"></textarea>
</bit-form-field>
</bit-card>
</bit-section>

View File

@ -0,0 +1,95 @@
import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import {
CardComponent,
CheckboxModule,
FormFieldModule,
IconButtonModule,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
} from "@bitwarden/components";
import { SendFormConfig } from "../../abstractions/send-form-config.service";
import { SendFormContainer } from "../../send-form-container";
@Component({
selector: "tools-send-options",
templateUrl: "./send-options.component.html",
standalone: true,
imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
JslibModule,
CardComponent,
FormFieldModule,
ReactiveFormsModule,
IconButtonModule,
CheckboxModule,
CommonModule,
],
})
export class SendOptionsComponent implements OnInit {
@Input({ required: true })
config: SendFormConfig;
@Input()
originalSendView: SendView;
sendOptionsForm = this.formBuilder.group({
maxAccessCount: [null as number],
accessCount: [null as number],
notes: [null as string],
password: [null as string],
hideEmail: [false as boolean],
});
get hasPassword(): boolean {
return (
this.sendOptionsForm.value.password !== null && this.sendOptionsForm.value.password !== ""
);
}
get shouldShowCount(): boolean {
return this.config.mode === "edit" && this.sendOptionsForm.value.maxAccessCount !== null;
}
get viewsLeft(): number {
return this.sendOptionsForm.value.maxAccessCount
? this.sendOptionsForm.value.maxAccessCount - this.sendOptionsForm.value.accessCount
: 0;
}
constructor(
private sendFormContainer: SendFormContainer,
private formBuilder: FormBuilder,
) {
this.sendFormContainer.registerChildForm("sendOptionsForm", this.sendOptionsForm);
this.sendOptionsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
this.sendFormContainer.patchSend((send) => {
Object.assign(send, {
maxAccessCount: value.maxAccessCount,
accessCount: value.accessCount,
password: value.password,
hideEmail: value.hideEmail,
notes: value.notes,
});
return send;
});
});
}
ngOnInit() {
if (this.sendFormContainer.originalSendView) {
this.sendOptionsForm.patchValue({
maxAccessCount: this.sendFormContainer.originalSendView.maxAccessCount,
accessCount: this.sendFormContainer.originalSendView.accessCount,
password: this.sendFormContainer.originalSendView.password,
hideEmail: this.sendFormContainer.originalSendView.hideEmail,
notes: this.sendFormContainer.originalSendView.notes,
});
}
}
}

View File

@ -1,7 +1,7 @@
import { DatePipe } from "@angular/common"; import { DatePipe } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core"; import { Component, Input, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, FormGroup, FormControl, Validators } from "@angular/forms"; import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
@ -9,11 +9,6 @@ import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendFormConfig } from "../../abstractions/send-form-config.service"; import { SendFormConfig } from "../../abstractions/send-form-config.service";
import { SendFormContainer } from "../../send-form-container"; import { SendFormContainer } from "../../send-form-container";
export type BaseSendDetailsForm = FormGroup<{
name: FormControl<string>;
selectedDeletionDatePreset: FormControl<string | number>;
}>;
// Value = hours // Value = hours
export enum DatePreset { export enum DatePreset {
OneHour = 1, OneHour = 1,
@ -38,21 +33,20 @@ export class BaseSendDetailsComponent implements OnInit {
@Input() config: SendFormConfig; @Input() config: SendFormConfig;
@Input() originalSendView?: SendView; @Input() originalSendView?: SendView;
sendDetailsForm: BaseSendDetailsForm;
customDeletionDateOption: DatePresetSelectOption | null = null; customDeletionDateOption: DatePresetSelectOption | null = null;
datePresetOptions: DatePresetSelectOption[] = []; datePresetOptions: DatePresetSelectOption[] = [];
sendDetailsForm = this.formBuilder.group({
name: new FormControl("", Validators.required),
selectedDeletionDatePreset: new FormControl(DatePreset.SevenDays || "", Validators.required),
});
constructor( constructor(
protected sendFormContainer: SendFormContainer, protected sendFormContainer: SendFormContainer,
protected formBuilder: FormBuilder, protected formBuilder: FormBuilder,
protected i18nService: I18nService, protected i18nService: I18nService,
protected datePipe: DatePipe, protected datePipe: DatePipe,
) { ) {
this.sendDetailsForm = this.formBuilder.group({
name: new FormControl("", Validators.required),
selectedDeletionDatePreset: new FormControl(DatePreset.SevenDays || "", Validators.required),
});
this.sendDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => { this.sendDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
this.sendFormContainer.patchSend((send) => { this.sendFormContainer.patchSend((send) => {
return Object.assign(send, { return Object.assign(send, {

View File

@ -1,5 +1,5 @@
<bit-section [formGroup]="sendDetailsForm"> <bit-section [formGroup]="sendDetailsForm">
<bit-section-header> <bit-section-header class="tw-mt-4">
<h2 bitTypography="h5">{{ "sendDetails" | i18n }}</h2> <h2 bitTypography="h5">{{ "sendDetails" | i18n }}</h2>
</bit-section-header> </bit-section-header>
@ -13,14 +13,12 @@
*ngIf="config.sendType === TextSendType" *ngIf="config.sendType === TextSendType"
[config]="config" [config]="config"
[originalSendView]="originalSendView" [originalSendView]="originalSendView"
[sendDetailsForm]="sendDetailsForm"
></tools-send-text-details> ></tools-send-text-details>
<tools-send-file-details <tools-send-file-details
*ngIf="config.sendType === FileSendType" *ngIf="config.sendType === FileSendType"
[config]="config" [config]="config"
[originalSendView]="originalSendView" [originalSendView]="originalSendView"
[sendDetailsForm]="sendDetailsForm"
></tools-send-file-details> ></tools-send-file-details>
<bit-form-field> <bit-form-field>
@ -39,4 +37,5 @@
<bit-hint>{{ "deletionDateDescV2" | i18n }}</bit-hint> <bit-hint>{{ "deletionDateDescV2" | i18n }}</bit-hint>
</bit-form-field> </bit-form-field>
</bit-card> </bit-card>
<tools-send-options [config]="config" [originalSendView]="originalSendView"></tools-send-options>
</bit-section> </bit-section>

View File

@ -17,6 +17,7 @@ import {
} from "@bitwarden/components"; } from "@bitwarden/components";
import { SendFormContainer } from "../../send-form-container"; import { SendFormContainer } from "../../send-form-container";
import { SendOptionsComponent } from "../options/send-options.component";
import { BaseSendDetailsComponent } from "./base-send-details.component"; import { BaseSendDetailsComponent } from "./base-send-details.component";
import { SendFileDetailsComponent } from "./send-file-details.component"; import { SendFileDetailsComponent } from "./send-file-details.component";
@ -36,6 +37,7 @@ import { SendTextDetailsComponent } from "./send-text-details.component";
ReactiveFormsModule, ReactiveFormsModule,
SendTextDetailsComponent, SendTextDetailsComponent,
SendFileDetailsComponent, SendFileDetailsComponent,
SendOptionsComponent,
IconButtonModule, IconButtonModule,
CheckboxModule, CheckboxModule,
CommonModule, CommonModule,

View File

@ -1,14 +1,7 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core"; import { Component, Input, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { import { FormBuilder, Validators, ReactiveFormsModule, FormsModule } from "@angular/forms";
FormBuilder,
FormControl,
FormGroup,
Validators,
ReactiveFormsModule,
FormsModule,
} from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
@ -19,14 +12,6 @@ import { ButtonModule, FormFieldModule, SectionComponent } from "@bitwarden/comp
import { SendFormConfig } from "../../abstractions/send-form-config.service"; import { SendFormConfig } from "../../abstractions/send-form-config.service";
import { SendFormContainer } from "../../send-form-container"; import { SendFormContainer } from "../../send-form-container";
import { BaseSendDetailsForm } from "./base-send-details.component";
type BaseSendFileDetailsForm = FormGroup<{
file: FormControl<SendFileView | null>;
}>;
export type SendFileDetailsForm = BaseSendFileDetailsForm & BaseSendDetailsForm;
@Component({ @Component({
selector: "tools-send-file-details", selector: "tools-send-file-details",
templateUrl: "./send-file-details.component.html", templateUrl: "./send-file-details.component.html",
@ -44,10 +29,10 @@ export type SendFileDetailsForm = BaseSendFileDetailsForm & BaseSendDetailsForm;
export class SendFileDetailsComponent implements OnInit { export class SendFileDetailsComponent implements OnInit {
@Input() config: SendFormConfig; @Input() config: SendFormConfig;
@Input() originalSendView?: SendView; @Input() originalSendView?: SendView;
@Input() sendDetailsForm: BaseSendDetailsForm;
baseSendFileDetailsForm: BaseSendFileDetailsForm; sendFileDetailsForm = this.formBuilder.group({
sendFileDetailsForm: SendFileDetailsForm; file: this.formBuilder.control<SendFileView | null>(null, Validators.required),
});
FileSendType = SendType.File; FileSendType = SendType.File;
fileName = ""; fileName = "";
@ -56,12 +41,6 @@ export class SendFileDetailsComponent implements OnInit {
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
protected sendFormContainer: SendFormContainer, protected sendFormContainer: SendFormContainer,
) { ) {
this.baseSendFileDetailsForm = this.formBuilder.group({
file: this.formBuilder.control<SendFileView | null>(null, Validators.required),
});
this.sendFileDetailsForm = Object.assign(this.baseSendFileDetailsForm, this.sendDetailsForm);
this.sendFormContainer.registerChildForm("sendFileDetailsForm", this.sendFileDetailsForm); this.sendFormContainer.registerChildForm("sendFileDetailsForm", this.sendFileDetailsForm);
this.sendFileDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => { this.sendFileDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {

View File

@ -1,4 +1,4 @@
<bit-section [formGroup]="sendTextDetailsForm"> <bit-section [formGroup]="sendTextDetailsForm" disableMargin>
<bit-form-field> <bit-form-field>
<bit-label>{{ "sendTypeTextToShare" | i18n }}</bit-label> <bit-label>{{ "sendTypeTextToShare" | i18n }}</bit-label>
<textarea bitInput id="text" rows="6" formControlName="text"></textarea> <textarea bitInput id="text" rows="6" formControlName="text"></textarea>

View File

@ -1,13 +1,7 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core"; import { Component, Input, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { import { FormBuilder, FormControl, Validators, ReactiveFormsModule } from "@angular/forms";
FormBuilder,
FormControl,
FormGroup,
Validators,
ReactiveFormsModule,
} from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
@ -16,15 +10,6 @@ import { CheckboxModule, FormFieldModule, SectionComponent } from "@bitwarden/co
import { SendFormConfig } from "../../abstractions/send-form-config.service"; import { SendFormConfig } from "../../abstractions/send-form-config.service";
import { SendFormContainer } from "../../send-form-container"; import { SendFormContainer } from "../../send-form-container";
import { BaseSendDetailsForm } from "./base-send-details.component";
type BaseSendTextDetailsForm = FormGroup<{
text: FormControl<string>;
hidden: FormControl<boolean>;
}>;
export type SendTextDetailsForm = BaseSendTextDetailsForm & BaseSendDetailsForm;
@Component({ @Component({
selector: "tools-send-text-details", selector: "tools-send-text-details",
templateUrl: "./send-text-details.component.html", templateUrl: "./send-text-details.component.html",
@ -41,22 +26,16 @@ export type SendTextDetailsForm = BaseSendTextDetailsForm & BaseSendDetailsForm;
export class SendTextDetailsComponent implements OnInit { export class SendTextDetailsComponent implements OnInit {
@Input() config: SendFormConfig; @Input() config: SendFormConfig;
@Input() originalSendView?: SendView; @Input() originalSendView?: SendView;
@Input() sendDetailsForm: BaseSendDetailsForm;
baseSendTextDetailsForm: BaseSendTextDetailsForm; sendTextDetailsForm = this.formBuilder.group({
sendTextDetailsForm: SendTextDetailsForm; text: new FormControl("", Validators.required),
hidden: new FormControl(false),
});
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
protected sendFormContainer: SendFormContainer, protected sendFormContainer: SendFormContainer,
) { ) {
this.baseSendTextDetailsForm = this.formBuilder.group({
text: new FormControl("", Validators.required),
hidden: new FormControl(false),
});
this.sendTextDetailsForm = Object.assign(this.baseSendTextDetailsForm, this.sendDetailsForm);
this.sendFormContainer.registerChildForm("sendTextDetailsForm", this.sendTextDetailsForm); this.sendFormContainer.registerChildForm("sendTextDetailsForm", this.sendTextDetailsForm);
this.sendTextDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => { this.sendTextDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
@ -73,7 +52,7 @@ export class SendTextDetailsComponent implements OnInit {
ngOnInit() { ngOnInit() {
if (this.originalSendView) { if (this.originalSendView) {
this.baseSendTextDetailsForm.patchValue({ this.sendTextDetailsForm.patchValue({
text: this.originalSendView.text?.text || "", text: this.originalSendView.text?.text || "",
hidden: this.originalSendView.text?.hidden || false, hidden: this.originalSendView.text?.hidden || false,
}); });

View File

@ -1,17 +1,19 @@
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendFormConfig } from "./abstractions/send-form-config.service"; import { SendFormConfig } from "./abstractions/send-form-config.service";
import { SendOptionsComponent } from "./components/options/send-options.component";
import { SendDetailsComponent } from "./components/send-details/send-details.component"; import { SendDetailsComponent } from "./components/send-details/send-details.component";
import { SendFileDetailsForm } from "./components/send-details/send-file-details.component"; import { SendFileDetailsComponent } from "./components/send-details/send-file-details.component";
import { SendTextDetailsForm } from "./components/send-details/send-text-details.component"; import { SendTextDetailsComponent } from "./components/send-details/send-text-details.component";
/** /**
* The complete form for a send. Includes all the sub-forms from their respective section components. * The complete form for a send. Includes all the sub-forms from their respective section components.
* TODO: Add additional form sections as they are implemented. * TODO: Add additional form sections as they are implemented.
*/ */
export type SendForm = { export type SendForm = {
sendDetailsForm?: SendDetailsComponent["sendDetailsForm"]; sendDetailsForm?: SendDetailsComponent["sendDetailsForm"];
sendTextDetailsForm?: SendTextDetailsForm; sendTextDetailsForm?: SendTextDetailsComponent["sendTextDetailsForm"];
sendFileDetailsForm?: SendFileDetailsForm; sendFileDetailsForm?: SendFileDetailsComponent["sendFileDetailsForm"];
sendOptionsForm?: SendOptionsComponent["sendOptionsForm"];
}; };
/** /**