diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 061929fc49..3c0d212bfc 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -531,6 +531,9 @@ "notes": { "message": "Notes" }, + "privateNote": { + "message": "Private note" + }, "note": { "message": "Note" }, @@ -2267,6 +2270,23 @@ "excludedDomainsSavedSuccess": { "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": { "message": "Send", "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.", "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": { "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." @@ -2514,6 +2538,9 @@ "hideEmail": { "message": "Hide my email address from recipients." }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, "sendOptionsPolicyInEffect": { "message": "One or more organization policies are affecting your Send options." }, diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html new file mode 100644 index 0000000000..e57b5af34c --- /dev/null +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html @@ -0,0 +1,31 @@ + + +

{{ "additionalOptions" | i18n }}

+
+ + + {{ "limitSendViews" | i18n }} + + {{ "limitSendViewsHint" | i18n }} +  {{ "limitSendViewsCount" | i18n: viewsLeft }} + + + {{ "password" | i18n }} + {{ "newPassword" | i18n }} + + + + {{ "sendPasswordDescV2" | i18n }} + + + + {{ "hideYourEmail" | i18n }} + + + {{ "privateNote" | i18n }} + + + +
diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts new file mode 100644 index 0000000000..46b0940c91 --- /dev/null +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts @@ -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, + }); + } + } +} diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts index f3e2229dd2..b5cf8ee0c7 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts @@ -1,7 +1,7 @@ import { DatePipe } from "@angular/common"; import { Component, Input, OnInit } from "@angular/core"; 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 { 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 { SendFormContainer } from "../../send-form-container"; -export type BaseSendDetailsForm = FormGroup<{ - name: FormControl; - selectedDeletionDatePreset: FormControl; -}>; - // Value = hours export enum DatePreset { OneHour = 1, @@ -38,21 +33,20 @@ export class BaseSendDetailsComponent implements OnInit { @Input() config: SendFormConfig; @Input() originalSendView?: SendView; - sendDetailsForm: BaseSendDetailsForm; customDeletionDateOption: DatePresetSelectOption | null = null; datePresetOptions: DatePresetSelectOption[] = []; + sendDetailsForm = this.formBuilder.group({ + name: new FormControl("", Validators.required), + selectedDeletionDatePreset: new FormControl(DatePreset.SevenDays || "", Validators.required), + }); + constructor( protected sendFormContainer: SendFormContainer, protected formBuilder: FormBuilder, protected i18nService: I18nService, 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.sendFormContainer.patchSend((send) => { return Object.assign(send, { diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html index 47e1fc6059..b10956daac 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html @@ -1,5 +1,5 @@ - +

{{ "sendDetails" | i18n }}

@@ -13,14 +13,12 @@ *ngIf="config.sendType === TextSendType" [config]="config" [originalSendView]="originalSendView" - [sendDetailsForm]="sendDetailsForm" > @@ -39,4 +37,5 @@ {{ "deletionDateDescV2" | i18n }} +
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts index aadfc590c2..2d00888b9b 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts @@ -17,6 +17,7 @@ import { } from "@bitwarden/components"; import { SendFormContainer } from "../../send-form-container"; +import { SendOptionsComponent } from "../options/send-options.component"; import { BaseSendDetailsComponent } from "./base-send-details.component"; import { SendFileDetailsComponent } from "./send-file-details.component"; @@ -36,6 +37,7 @@ import { SendTextDetailsComponent } from "./send-text-details.component"; ReactiveFormsModule, SendTextDetailsComponent, SendFileDetailsComponent, + SendOptionsComponent, IconButtonModule, CheckboxModule, CommonModule, diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts index 86c9fa96f1..61391e2072 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts @@ -1,14 +1,7 @@ import { CommonModule } from "@angular/common"; import { Component, Input, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { - FormBuilder, - FormControl, - FormGroup, - Validators, - ReactiveFormsModule, - FormsModule, -} from "@angular/forms"; +import { FormBuilder, Validators, ReactiveFormsModule, FormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; 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 { SendFormContainer } from "../../send-form-container"; -import { BaseSendDetailsForm } from "./base-send-details.component"; - -type BaseSendFileDetailsForm = FormGroup<{ - file: FormControl; -}>; - -export type SendFileDetailsForm = BaseSendFileDetailsForm & BaseSendDetailsForm; - @Component({ selector: "tools-send-file-details", templateUrl: "./send-file-details.component.html", @@ -44,10 +29,10 @@ export type SendFileDetailsForm = BaseSendFileDetailsForm & BaseSendDetailsForm; export class SendFileDetailsComponent implements OnInit { @Input() config: SendFormConfig; @Input() originalSendView?: SendView; - @Input() sendDetailsForm: BaseSendDetailsForm; - baseSendFileDetailsForm: BaseSendFileDetailsForm; - sendFileDetailsForm: SendFileDetailsForm; + sendFileDetailsForm = this.formBuilder.group({ + file: this.formBuilder.control(null, Validators.required), + }); FileSendType = SendType.File; fileName = ""; @@ -56,12 +41,6 @@ export class SendFileDetailsComponent implements OnInit { private formBuilder: FormBuilder, protected sendFormContainer: SendFormContainer, ) { - this.baseSendFileDetailsForm = this.formBuilder.group({ - file: this.formBuilder.control(null, Validators.required), - }); - - this.sendFileDetailsForm = Object.assign(this.baseSendFileDetailsForm, this.sendDetailsForm); - this.sendFormContainer.registerChildForm("sendFileDetailsForm", this.sendFileDetailsForm); this.sendFileDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => { diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.html b/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.html index bddd8f226d..b7875d1d45 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.html @@ -1,4 +1,4 @@ - + {{ "sendTypeTextToShare" | i18n }} diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts index 970c74644d..85fc324f2f 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts @@ -1,13 +1,7 @@ import { CommonModule } from "@angular/common"; import { Component, Input, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { - FormBuilder, - FormControl, - FormGroup, - Validators, - ReactiveFormsModule, -} from "@angular/forms"; +import { FormBuilder, FormControl, Validators, ReactiveFormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; 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 { SendFormContainer } from "../../send-form-container"; -import { BaseSendDetailsForm } from "./base-send-details.component"; - -type BaseSendTextDetailsForm = FormGroup<{ - text: FormControl; - hidden: FormControl; -}>; - -export type SendTextDetailsForm = BaseSendTextDetailsForm & BaseSendDetailsForm; - @Component({ selector: "tools-send-text-details", templateUrl: "./send-text-details.component.html", @@ -41,22 +26,16 @@ export type SendTextDetailsForm = BaseSendTextDetailsForm & BaseSendDetailsForm; export class SendTextDetailsComponent implements OnInit { @Input() config: SendFormConfig; @Input() originalSendView?: SendView; - @Input() sendDetailsForm: BaseSendDetailsForm; - baseSendTextDetailsForm: BaseSendTextDetailsForm; - sendTextDetailsForm: SendTextDetailsForm; + sendTextDetailsForm = this.formBuilder.group({ + text: new FormControl("", Validators.required), + hidden: new FormControl(false), + }); constructor( private formBuilder: FormBuilder, 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.sendTextDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => { @@ -73,7 +52,7 @@ export class SendTextDetailsComponent implements OnInit { ngOnInit() { if (this.originalSendView) { - this.baseSendTextDetailsForm.patchValue({ + this.sendTextDetailsForm.patchValue({ text: this.originalSendView.text?.text || "", hidden: this.originalSendView.text?.hidden || false, }); diff --git a/libs/tools/send/send-ui/src/send-form/send-form-container.ts b/libs/tools/send/send-ui/src/send-form/send-form-container.ts index 21508d9672..84e0913194 100644 --- a/libs/tools/send/send-ui/src/send-form/send-form-container.ts +++ b/libs/tools/send/send-ui/src/send-form/send-form-container.ts @@ -1,17 +1,19 @@ import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; 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 { SendFileDetailsForm } from "./components/send-details/send-file-details.component"; -import { SendTextDetailsForm } from "./components/send-details/send-text-details.component"; +import { SendFileDetailsComponent } from "./components/send-details/send-file-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. * TODO: Add additional form sections as they are implemented. */ export type SendForm = { sendDetailsForm?: SendDetailsComponent["sendDetailsForm"]; - sendTextDetailsForm?: SendTextDetailsForm; - sendFileDetailsForm?: SendFileDetailsForm; + sendTextDetailsForm?: SendTextDetailsComponent["sendTextDetailsForm"]; + sendFileDetailsForm?: SendFileDetailsComponent["sendFileDetailsForm"]; + sendOptionsForm?: SendOptionsComponent["sendOptionsForm"]; }; /**