[PM-11903] - add file send component (#11132)
* wip - send file details * wip - file send * send file details * fix click on send list container * remove popup code * remove popup code * finalize send file details * address PR feedback. add base form to send form * revert changes to send list items container * revert changes to send list items container --------- Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com>
This commit is contained in:
parent
2b85392b0f
commit
00f2317a82
|
@ -1136,6 +1136,9 @@
|
|||
"file": {
|
||||
"message": "File"
|
||||
},
|
||||
"fileToShare": {
|
||||
"message": "File to share"
|
||||
},
|
||||
"selectFile": {
|
||||
"message": "Select a file"
|
||||
},
|
||||
|
|
|
@ -117,25 +117,18 @@ export class SendAddEditComponent {
|
|||
)
|
||||
.subscribe((config) => {
|
||||
this.config = config;
|
||||
this.headerText = this.getHeaderText(config.mode, config.sendType);
|
||||
this.headerText = this.getHeaderText(config.mode);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the header text based on the mode and type.
|
||||
* Gets the header text based on the mode.
|
||||
* @param mode The mode of the send form.
|
||||
* @param type The type of the send form.
|
||||
* @returns The header text.
|
||||
*/
|
||||
private getHeaderText(mode: SendFormMode, type: SendType) {
|
||||
const headerKey =
|
||||
mode === "edit" || mode === "partial-edit" ? "editItemHeader" : "newItemHeader";
|
||||
|
||||
switch (type) {
|
||||
case SendType.Text:
|
||||
return this.i18nService.t(headerKey, this.i18nService.t("sendTypeText"));
|
||||
case SendType.File:
|
||||
return this.i18nService.t(headerKey, this.i18nService.t("sendTypeFile"));
|
||||
}
|
||||
private getHeaderText(mode: SendFormMode) {
|
||||
return this.i18nService.t(
|
||||
mode === "edit" || mode === "partial-edit" ? "editSend" : "createSend",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ export class BaseSendDetailsComponent implements OnInit {
|
|||
} as SendView);
|
||||
});
|
||||
});
|
||||
|
||||
this.sendFormContainer.registerChildForm("sendDetailsForm", this.sendDetailsForm);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
|
|
@ -16,6 +16,13 @@
|
|||
[sendDetailsForm]="sendDetailsForm"
|
||||
></tools-send-text-details>
|
||||
|
||||
<tools-send-file-details
|
||||
*ngIf="config.sendType === FileSendType"
|
||||
[config]="config"
|
||||
[originalSendView]="originalSendView"
|
||||
[sendDetailsForm]="sendDetailsForm"
|
||||
></tools-send-file-details>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "deletionDate" | i18n }}</bit-label>
|
||||
<bit-select
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import { SendFormContainer } from "../../send-form-container";
|
||||
|
||||
import { BaseSendDetailsComponent } from "./base-send-details.component";
|
||||
import { SendFileDetailsComponent } from "./send-file-details.component";
|
||||
import { SendTextDetailsComponent } from "./send-text-details.component";
|
||||
|
||||
@Component({
|
||||
|
@ -34,6 +35,7 @@ import { SendTextDetailsComponent } from "./send-text-details.component";
|
|||
FormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
SendTextDetailsComponent,
|
||||
SendFileDetailsComponent,
|
||||
IconButtonModule,
|
||||
CheckboxModule,
|
||||
CommonModule,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<bit-section [formGroup]="sendFileDetailsForm">
|
||||
<div *ngIf="config.mode === 'edit'">
|
||||
<div class="tw-text-muted">{{ "file" | i18n }}</div>
|
||||
<div>{{ originalSendView.file.fileName }}</div>
|
||||
<div class="tw-text-muted">{{ originalSendView.file.sizeName }}</div>
|
||||
</div>
|
||||
<bit-form-field *ngIf="config.mode !== 'edit'">
|
||||
<bit-label for="file">{{ "fileToShare" | i18n }}</bit-label>
|
||||
<button bitButton type="button" buttonType="primary" (click)="fileSelector.click()">
|
||||
{{ "chooseFile" | i18n }}
|
||||
</button>
|
||||
<span
|
||||
class="tw-flex tw-items-center tw-pl-3"
|
||||
[ngClass]="fileName ? 'tw-text-main' : 'tw-text-muted'"
|
||||
>
|
||||
{{ fileName || ("noFileChosen" | i18n) }}</span
|
||||
>
|
||||
<input
|
||||
bitInput
|
||||
#fileSelector
|
||||
type="file"
|
||||
formControlName="file"
|
||||
hidden
|
||||
(change)="onFileSelected($event)"
|
||||
/>
|
||||
<bit-hint>
|
||||
{{ "maxFileSize" | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-field>
|
||||
</bit-section>
|
|
@ -0,0 +1,92 @@
|
|||
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 { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
import { SendFileView } from "@bitwarden/common/tools/send/models/view/send-file.view";
|
||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||
import { ButtonModule, FormFieldModule, SectionComponent } from "@bitwarden/components";
|
||||
|
||||
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<SendFileView | null>;
|
||||
}>;
|
||||
|
||||
export type SendFileDetailsForm = BaseSendFileDetailsForm & BaseSendDetailsForm;
|
||||
|
||||
@Component({
|
||||
selector: "tools-send-file-details",
|
||||
templateUrl: "./send-file-details.component.html",
|
||||
standalone: true,
|
||||
imports: [
|
||||
ButtonModule,
|
||||
CommonModule,
|
||||
JslibModule,
|
||||
ReactiveFormsModule,
|
||||
FormFieldModule,
|
||||
SectionComponent,
|
||||
FormsModule,
|
||||
],
|
||||
})
|
||||
export class SendFileDetailsComponent implements OnInit {
|
||||
@Input() config: SendFormConfig;
|
||||
@Input() originalSendView?: SendView;
|
||||
@Input() sendDetailsForm: BaseSendDetailsForm;
|
||||
|
||||
baseSendFileDetailsForm: BaseSendFileDetailsForm;
|
||||
sendFileDetailsForm: SendFileDetailsForm;
|
||||
|
||||
FileSendType = SendType.File;
|
||||
fileName = "";
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
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.sendFileDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
|
||||
this.sendFormContainer.patchSend((send) => {
|
||||
return Object.assign(send, {
|
||||
file: value.file,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onFileSelected = (event: Event): void => {
|
||||
const file = (event.target as HTMLInputElement).files?.[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
this.fileName = file.name;
|
||||
this.sendFormContainer.onFileSelected(file);
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
if (this.originalSendView) {
|
||||
this.sendFileDetailsForm.patchValue({
|
||||
file: this.originalSendView.file,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
|
|||
private bitSubmit: BitSubmitDirective;
|
||||
private destroyRef = inject(DestroyRef);
|
||||
private _firstInitialized = false;
|
||||
private file: File | null = null;
|
||||
|
||||
/**
|
||||
* The form ID to use for the form. Used to connect it to a submit button.
|
||||
|
@ -188,14 +189,17 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
|
|||
private i18nService: I18nService,
|
||||
) {}
|
||||
|
||||
onFileSelected(file: File): void {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
if (this.sendForm.invalid) {
|
||||
this.sendForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Add file handling
|
||||
await this.addEditFormService.saveSend(this.updatedSendView, null, this.config);
|
||||
await this.addEditFormService.saveSend(this.updatedSendView, this.file, this.config);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
|
|
|
@ -2,6 +2,7 @@ import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
|||
|
||||
import { SendFormConfig } from "./abstractions/send-form-config.service";
|
||||
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";
|
||||
/**
|
||||
* The complete form for a send. Includes all the sub-forms from their respective section components.
|
||||
|
@ -10,6 +11,7 @@ import { SendTextDetailsForm } from "./components/send-details/send-text-details
|
|||
export type SendForm = {
|
||||
sendDetailsForm?: SendDetailsComponent["sendDetailsForm"];
|
||||
sendTextDetailsForm?: SendTextDetailsForm;
|
||||
sendFileDetailsForm?: SendFileDetailsForm;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -37,5 +39,7 @@ export abstract class SendFormContainer {
|
|||
group: Exclude<SendForm[K], undefined>,
|
||||
): void;
|
||||
|
||||
abstract onFileSelected(file: File): void;
|
||||
|
||||
abstract patchSend(updateFn: (current: SendView) => SendView): void;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue