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

[PM-11926] - send created redirect (#11140)

* send created redirect

* fix test

* fix test

* fix send form save

* return SendData from saveSend

* When saving a Send, bubble up a SendView which can be passed to the SendCreated component

* Use events to initiate navigation and move actual navigation into client-specific component

---------

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
Jordan Aasen 2024-10-01 12:58:00 -07:00 committed by GitHub
parent ab5a02f483
commit dab60dbaea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 62 additions and 28 deletions

View File

@ -4,7 +4,8 @@
<tools-send-form
formId="sendForm"
[config]="config"
(sendSaved)="onSendSaved()"
(onSendCreated)="onSendCreated($event)"
(onSendUpdated)="onSendUpdated($event)"
[submitBtn]="submitBtn"
>
</tools-send-form>

View File

@ -2,12 +2,13 @@ import { CommonModule, Location } from "@angular/common";
import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormsModule } from "@angular/forms";
import { ActivatedRoute, Params } from "@angular/router";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { map, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendId } from "@bitwarden/common/types/guid";
import {
@ -95,14 +96,25 @@ export class SendAddEditComponent {
private sendApiService: SendApiService,
private toastService: ToastService,
private dialogService: DialogService,
private router: Router,
) {
this.subscribeToParams();
}
/**
* Handles the event when the send is saved.
* Handles the event when the send is created.
*/
onSendSaved() {
async onSendCreated(send: SendView) {
await this.router.navigate(["/send-created"], {
queryParams: { sendId: send.id },
});
return;
}
/**
* Handles the event when the send is updated.
*/
onSendUpdated(send: SendView) {
this.location.back();
}

View File

@ -1,6 +1,11 @@
<main class="tw-top-0">
<popup-page>
<popup-header slot="header" [pageTitle]="'createdSend' | i18n" showBackButton>
<popup-header
slot="header"
[pageTitle]="'createdSend' | i18n"
showBackButton
[backAction]="close.bind(this)"
>
<ng-container slot="end">
<app-pop-out></app-pop-out>
</ng-container>

View File

@ -1,6 +1,6 @@
import { CommonModule, Location } from "@angular/common";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ActivatedRoute, RouterLink } from "@angular/router";
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
import { RouterTestingModule } from "@angular/router/testing";
import { MockProxy, mock } from "jest-mock-extended";
import { of } from "rxjs";
@ -33,6 +33,7 @@ describe("SendCreatedComponent", () => {
let location: MockProxy<Location>;
let activatedRoute: MockProxy<ActivatedRoute>;
let environmentService: MockProxy<EnvironmentService>;
let router: MockProxy<Router>;
const sendId = "test-send-id";
const deletionDate = new Date();
@ -52,6 +53,7 @@ describe("SendCreatedComponent", () => {
location = mock<Location>();
activatedRoute = mock<ActivatedRoute>();
environmentService = mock<EnvironmentService>();
router = mock<Router>();
Object.defineProperty(environmentService, "environment$", {
configurable: true,
get: () => of(new SelfHostedEnvironment({ webVault: "https://example.com" })),
@ -89,6 +91,7 @@ describe("SendCreatedComponent", () => {
{ provide: ConfigService, useValue: mock<ConfigService>() },
{ provide: EnvironmentService, useValue: environmentService },
{ provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() },
{ provide: Router, useValue: router },
],
}).compileComponents();
});
@ -109,10 +112,10 @@ describe("SendCreatedComponent", () => {
expect(component["daysAvailable"]).toBe(7);
});
it("should navigate back on close", () => {
it("should navigate back to send list on close", async () => {
fixture.detectChanges();
component.close();
expect(location.back).toHaveBeenCalled();
await component.close();
expect(router.navigate).toHaveBeenCalledWith(["/tabs/send"]);
});
describe("getDaysAvailable", () => {

View File

@ -1,7 +1,7 @@
import { CommonModule, Location } from "@angular/common";
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, RouterLink } from "@angular/router";
import { ActivatedRoute, Router, RouterLink, RouterModule } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
@ -30,6 +30,7 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page
PopupHeaderComponent,
PopupPageComponent,
RouterLink,
RouterModule,
PopupFooterComponent,
IconModule,
],
@ -45,10 +46,11 @@ export class SendCreatedComponent {
private sendService: SendService,
private route: ActivatedRoute,
private toastService: ToastService,
private location: Location,
private router: Router,
private environmentService: EnvironmentService,
) {
const sendId = this.route.snapshot.queryParamMap.get("sendId");
this.sendService.sendViews$.pipe(takeUntilDestroyed()).subscribe((sendViews) => {
this.send = sendViews.find((s) => s.id === sendId);
if (this.send) {
@ -62,8 +64,8 @@ export class SendCreatedComponent {
return Math.max(0, Math.ceil((send.deletionDate.getTime() - now) / (1000 * 60 * 60 * 24)));
}
close() {
this.location.back();
async close() {
await this.router.navigate(["/tabs/send"]);
}
async copyLink() {

View File

@ -36,5 +36,5 @@ export abstract class SendApiService {
renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise<SendFileUploadDataResponse>;
removePassword: (id: string) => Promise<any>;
delete: (id: string) => Promise<any>;
save: (sendData: [Send, EncArrayBuffer]) => Promise<any>;
save: (sendData: [Send, EncArrayBuffer]) => Promise<Send>;
}

View File

@ -135,11 +135,12 @@ export class SendApiService implements SendApiServiceAbstraction {
return this.apiService.send("DELETE", "/sends/" + id, null, true, false);
}
async save(sendData: [Send, EncArrayBuffer]): Promise<any> {
async save(sendData: [Send, EncArrayBuffer]): Promise<Send> {
const response = await this.upload(sendData);
const data = new SendData(response);
await this.sendService.upsert(data);
return new Send(data);
}
async delete(id: string): Promise<any> {

View File

@ -85,9 +85,14 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
submitBtn?: ButtonComponent;
/**
* Event emitted when the send is saved successfully.
* Event emitted when the send is created successfully.
*/
@Output() sendSaved = new EventEmitter<SendView>();
@Output() onSendCreated = new EventEmitter<SendView>();
/**
* Event emitted when the send is updated successfully.
*/
@Output() onSendUpdated = new EventEmitter<SendView>();
/**
* The original send being edited or cloned. Null for add mode.
@ -200,22 +205,26 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
return;
}
const sendView = await this.addEditFormService.saveSend(
this.updatedSendView,
this.file,
this.config,
);
if (this.config.mode === "add") {
this.onSendCreated.emit(sendView);
return;
}
if (Utils.isNullOrWhitespace(this.updatedSendView.password)) {
this.updatedSendView.password = null;
}
await this.addEditFormService.saveSend(this.updatedSendView, this.file, this.config);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.config.mode === "edit" || this.config.mode === "partial-edit"
? "editedItem"
: "addedItem",
),
message: this.i18nService.t("editedItem"),
});
this.sendSaved.emit(this.updatedSendView);
this.onSendUpdated.emit(this.updatedSendView);
};
}

View File

@ -19,6 +19,7 @@ export class DefaultSendFormService implements SendFormService {
async saveSend(send: SendView, file: File | ArrayBuffer, config: SendFormConfig) {
const sendData = await this.sendService.encrypt(send, file, send.password, null);
return await this.sendApiService.save(sendData);
const newSend = await this.sendApiService.save(sendData);
return await this.decryptSend(newSend);
}
}