mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00: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:
@@ -4,7 +4,8 @@
|
|||||||
<tools-send-form
|
<tools-send-form
|
||||||
formId="sendForm"
|
formId="sendForm"
|
||||||
[config]="config"
|
[config]="config"
|
||||||
(sendSaved)="onSendSaved()"
|
(onSendCreated)="onSendCreated($event)"
|
||||||
|
(onSendUpdated)="onSendUpdated($event)"
|
||||||
[submitBtn]="submitBtn"
|
[submitBtn]="submitBtn"
|
||||||
>
|
>
|
||||||
</tools-send-form>
|
</tools-send-form>
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { CommonModule, Location } from "@angular/common";
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
import { ActivatedRoute, Params } from "@angular/router";
|
import { ActivatedRoute, Params, Router } from "@angular/router";
|
||||||
import { map, switchMap } from "rxjs";
|
import { map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
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 { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
import { SendId } from "@bitwarden/common/types/guid";
|
import { SendId } from "@bitwarden/common/types/guid";
|
||||||
import {
|
import {
|
||||||
@@ -95,14 +96,25 @@ export class SendAddEditComponent {
|
|||||||
private sendApiService: SendApiService,
|
private sendApiService: SendApiService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
|
private router: Router,
|
||||||
) {
|
) {
|
||||||
this.subscribeToParams();
|
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();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
<main class="tw-top-0">
|
<main class="tw-top-0">
|
||||||
<popup-page>
|
<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">
|
<ng-container slot="end">
|
||||||
<app-pop-out></app-pop-out>
|
<app-pop-out></app-pop-out>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CommonModule, Location } from "@angular/common";
|
import { CommonModule, Location } from "@angular/common";
|
||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
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 { RouterTestingModule } from "@angular/router/testing";
|
||||||
import { MockProxy, mock } from "jest-mock-extended";
|
import { MockProxy, mock } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
@@ -33,6 +33,7 @@ describe("SendCreatedComponent", () => {
|
|||||||
let location: MockProxy<Location>;
|
let location: MockProxy<Location>;
|
||||||
let activatedRoute: MockProxy<ActivatedRoute>;
|
let activatedRoute: MockProxy<ActivatedRoute>;
|
||||||
let environmentService: MockProxy<EnvironmentService>;
|
let environmentService: MockProxy<EnvironmentService>;
|
||||||
|
let router: MockProxy<Router>;
|
||||||
|
|
||||||
const sendId = "test-send-id";
|
const sendId = "test-send-id";
|
||||||
const deletionDate = new Date();
|
const deletionDate = new Date();
|
||||||
@@ -52,6 +53,7 @@ describe("SendCreatedComponent", () => {
|
|||||||
location = mock<Location>();
|
location = mock<Location>();
|
||||||
activatedRoute = mock<ActivatedRoute>();
|
activatedRoute = mock<ActivatedRoute>();
|
||||||
environmentService = mock<EnvironmentService>();
|
environmentService = mock<EnvironmentService>();
|
||||||
|
router = mock<Router>();
|
||||||
Object.defineProperty(environmentService, "environment$", {
|
Object.defineProperty(environmentService, "environment$", {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: () => of(new SelfHostedEnvironment({ webVault: "https://example.com" })),
|
get: () => of(new SelfHostedEnvironment({ webVault: "https://example.com" })),
|
||||||
@@ -89,6 +91,7 @@ describe("SendCreatedComponent", () => {
|
|||||||
{ provide: ConfigService, useValue: mock<ConfigService>() },
|
{ provide: ConfigService, useValue: mock<ConfigService>() },
|
||||||
{ provide: EnvironmentService, useValue: environmentService },
|
{ provide: EnvironmentService, useValue: environmentService },
|
||||||
{ provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() },
|
{ provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
@@ -109,10 +112,10 @@ describe("SendCreatedComponent", () => {
|
|||||||
expect(component["daysAvailable"]).toBe(7);
|
expect(component["daysAvailable"]).toBe(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should navigate back on close", () => {
|
it("should navigate back to send list on close", async () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.close();
|
await component.close();
|
||||||
expect(location.back).toHaveBeenCalled();
|
expect(router.navigate).toHaveBeenCalledWith(["/tabs/send"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getDaysAvailable", () => {
|
describe("getDaysAvailable", () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CommonModule, Location } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
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 { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
@@ -30,6 +30,7 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page
|
|||||||
PopupHeaderComponent,
|
PopupHeaderComponent,
|
||||||
PopupPageComponent,
|
PopupPageComponent,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
|
RouterModule,
|
||||||
PopupFooterComponent,
|
PopupFooterComponent,
|
||||||
IconModule,
|
IconModule,
|
||||||
],
|
],
|
||||||
@@ -45,10 +46,11 @@ export class SendCreatedComponent {
|
|||||||
private sendService: SendService,
|
private sendService: SendService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private location: Location,
|
private router: Router,
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
) {
|
) {
|
||||||
const sendId = this.route.snapshot.queryParamMap.get("sendId");
|
const sendId = this.route.snapshot.queryParamMap.get("sendId");
|
||||||
|
|
||||||
this.sendService.sendViews$.pipe(takeUntilDestroyed()).subscribe((sendViews) => {
|
this.sendService.sendViews$.pipe(takeUntilDestroyed()).subscribe((sendViews) => {
|
||||||
this.send = sendViews.find((s) => s.id === sendId);
|
this.send = sendViews.find((s) => s.id === sendId);
|
||||||
if (this.send) {
|
if (this.send) {
|
||||||
@@ -62,8 +64,8 @@ export class SendCreatedComponent {
|
|||||||
return Math.max(0, Math.ceil((send.deletionDate.getTime() - now) / (1000 * 60 * 60 * 24)));
|
return Math.max(0, Math.ceil((send.deletionDate.getTime() - now) / (1000 * 60 * 60 * 24)));
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
async close() {
|
||||||
this.location.back();
|
await this.router.navigate(["/tabs/send"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyLink() {
|
async copyLink() {
|
||||||
|
|||||||
@@ -36,5 +36,5 @@ export abstract class SendApiService {
|
|||||||
renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise<SendFileUploadDataResponse>;
|
renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise<SendFileUploadDataResponse>;
|
||||||
removePassword: (id: string) => Promise<any>;
|
removePassword: (id: string) => Promise<any>;
|
||||||
delete: (id: string) => Promise<any>;
|
delete: (id: string) => Promise<any>;
|
||||||
save: (sendData: [Send, EncArrayBuffer]) => Promise<any>;
|
save: (sendData: [Send, EncArrayBuffer]) => Promise<Send>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,11 +135,12 @@ export class SendApiService implements SendApiServiceAbstraction {
|
|||||||
return this.apiService.send("DELETE", "/sends/" + id, null, true, false);
|
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 response = await this.upload(sendData);
|
||||||
|
|
||||||
const data = new SendData(response);
|
const data = new SendData(response);
|
||||||
await this.sendService.upsert(data);
|
await this.sendService.upsert(data);
|
||||||
|
return new Send(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<any> {
|
async delete(id: string): Promise<any> {
|
||||||
|
|||||||
@@ -85,9 +85,14 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
|
|||||||
submitBtn?: ButtonComponent;
|
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.
|
* The original send being edited or cloned. Null for add mode.
|
||||||
@@ -200,22 +205,26 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
|
|||||||
return;
|
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)) {
|
if (Utils.isNullOrWhitespace(this.updatedSendView.password)) {
|
||||||
this.updatedSendView.password = null;
|
this.updatedSendView.password = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.addEditFormService.saveSend(this.updatedSendView, this.file, this.config);
|
|
||||||
|
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: null,
|
||||||
message: this.i18nService.t(
|
message: this.i18nService.t("editedItem"),
|
||||||
this.config.mode === "edit" || this.config.mode === "partial-edit"
|
|
||||||
? "editedItem"
|
|
||||||
: "addedItem",
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
this.onSendUpdated.emit(this.updatedSendView);
|
||||||
this.sendSaved.emit(this.updatedSendView);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class DefaultSendFormService implements SendFormService {
|
|||||||
|
|
||||||
async saveSend(send: SendView, file: File | ArrayBuffer, config: SendFormConfig) {
|
async saveSend(send: SendView, file: File | ArrayBuffer, config: SendFormConfig) {
|
||||||
const sendData = await this.sendService.encrypt(send, file, send.password, null);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user