1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 07:13:32 +00:00
Files
browser/src/popup/vault/view.component.ts
Matt Gibson 0cd6efd67f Move share from edit to view. Fix animations (#1497)
* Move share from edit to view. Fix animations

Editing and Sharing a cipher simultaneously results in lost edits. Move
share button to the view page to resolve this confusion.

Previous routing caused the share form to be animated again on
submition, resulting in a stuttering page load. This method correctly
animates all transitions with the concession that the share page
always takes you back to the view page. This is not necessarily the current
behavior, but it is the most likely behavior in the current scheme

* Update jslib reference
2020-12-17 11:06:31 -06:00

245 lines
8.2 KiB
TypeScript

import { Location } from '@angular/common';
import {
ChangeDetectorRef,
Component,
NgZone,
} from '@angular/core';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { EventService } from 'jslib/abstractions/event.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { TokenService } from 'jslib/abstractions/token.service';
import { TotpService } from 'jslib/abstractions/totp.service';
import { UserService } from 'jslib/abstractions/user.service';
import { Cipher } from 'jslib/models/domain';
import { LoginUriView } from 'jslib/models/view';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { ViewComponent as BaseViewComponent } from 'jslib/angular/components/view.component';
import { BrowserApi } from '../../browser/browserApi';
import { AutofillService } from '../../services/abstractions/autofill.service';
import { PopupUtilsService } from '../services/popup-utils.service';
const BroadcasterSubscriptionId = 'ChildViewComponent';
@Component({
selector: 'app-vault-view',
templateUrl: 'view.component.html',
})
export class ViewComponent extends BaseViewComponent {
showAttachments = true;
pageDetails: any[] = [];
tab: any;
loadPageDetailsTimeout: number;
inPopout = false;
constructor(cipherService: CipherService, totpService: TotpService,
tokenService: TokenService, i18nService: I18nService,
cryptoService: CryptoService, platformUtilsService: PlatformUtilsService,
auditService: AuditService, private route: ActivatedRoute,
private router: Router, private location: Location,
broadcasterService: BroadcasterService, ngZone: NgZone,
changeDetectorRef: ChangeDetectorRef, userService: UserService,
eventService: EventService, private autofillService: AutofillService,
private messagingService: MessagingService, private popupUtilsService: PopupUtilsService) {
super(cipherService, totpService, tokenService, i18nService, cryptoService, platformUtilsService,
auditService, window, broadcasterService, ngZone, changeDetectorRef, userService, eventService);
}
ngOnInit() {
this.inPopout = this.popupUtilsService.inPopout(window);
const queryParamsSub = this.route.queryParams.subscribe(async (params) => {
if (params.cipherId) {
this.cipherId = params.cipherId;
} else {
this.close();
}
await this.load();
if (queryParamsSub != null) {
queryParamsSub.unsubscribe();
}
});
super.ngOnInit();
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case 'collectPageDetailsResponse':
if (message.sender === BroadcasterSubscriptionId) {
this.pageDetails.push({
frameId: message.webExtSender.frameId,
tab: message.tab,
details: message.details,
});
}
break;
case 'tabChanged':
case 'windowChanged':
if (this.loadPageDetailsTimeout != null) {
window.clearTimeout(this.loadPageDetailsTimeout);
}
this.loadPageDetailsTimeout = window.setTimeout(() => this.loadPageDetails(), 500);
break;
default:
break;
}
});
});
}
ngOnDestroy() {
super.ngOnDestroy();
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
async load() {
await super.load();
await this.loadPageDetails();
}
edit() {
if (this.cipher.isDeleted) {
return false;
}
super.edit();
this.router.navigate(['/edit-cipher'], { queryParams: { cipherId: this.cipher.id } });
}
clone() {
if (this.cipher.isDeleted) {
return false;
}
super.clone();
this.router.navigate(['/clone-cipher'], {
queryParams: {
cloneMode: true,
cipherId: this.cipher.id,
},
});
}
share() {
super.share();
if (this.cipher.organizationId == null) {
this.router.navigate(['/share-cipher'], { replaceUrl: true, queryParams: { cipherId: this.cipher.id } });
}
}
async fillCipher() {
const didAutofill = await this.doAutofill();
if (didAutofill) {
this.platformUtilsService.showToast('success', null,
this.i18nService.t('autoFillSuccess'));
}
}
async fillCipherAndSave() {
const didAutofill = await this.doAutofill();
if (didAutofill) {
if (this.tab == null) {
throw new Error('No tab found.');
}
if (this.cipher.login.uris == null) {
this.cipher.login.uris = [];
} else {
if (this.cipher.login.uris.some((uri) => uri.uri === this.tab.url)) {
this.platformUtilsService.showToast('success', null,
this.i18nService.t('autoFillSuccessAndSavedUri'));
return;
}
}
const loginUri = new LoginUriView();
loginUri.uri = this.tab.url;
this.cipher.login.uris.push(loginUri);
try {
const cipher: Cipher = await this.cipherService.encrypt(this.cipher);
await this.cipherService.saveWithServer(cipher);
this.platformUtilsService.showToast('success', null,
this.i18nService.t('autoFillSuccessAndSavedUri'));
this.messagingService.send('editedCipher');
} catch {
this.platformUtilsService.showToast('error', null,
this.i18nService.t('unexpectedError'));
}
}
}
async restore() {
if (!this.cipher.isDeleted) {
return false;
}
if (await super.restore()) {
this.close();
return true;
}
return false;
}
async delete() {
if (await super.delete()) {
this.close();
return true;
}
return false;
}
close() {
this.location.back();
}
private async loadPageDetails() {
this.pageDetails = [];
this.tab = await BrowserApi.getTabFromCurrentWindow();
if (this.tab == null) {
return;
}
BrowserApi.tabSendMessage(this.tab, {
command: 'collectPageDetails',
tab: this.tab,
sender: BroadcasterSubscriptionId,
});
}
private async doAutofill() {
if (this.pageDetails == null || this.pageDetails.length === 0) {
this.platformUtilsService.showToast('error', null,
this.i18nService.t('autofillError'));
return false;
}
try {
this.totpCode = await this.autofillService.doAutoFill({
cipher: this.cipher,
pageDetails: this.pageDetails,
doc: window.document,
fillNewPassword: true,
});
if (this.totpCode != null) {
this.platformUtilsService.copyToClipboard(this.totpCode, { window: window });
}
} catch {
this.platformUtilsService.showToast('error', null,
this.i18nService.t('autofillError'));
this.changeDetectorRef.detectChanges();
return false;
}
return true;
}
}