mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-02 18:17:46 +01:00
cipher attachments modal
This commit is contained in:
parent
3db86e2a6b
commit
d256a872fc
@ -1,49 +1,45 @@
|
||||
<div class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">
|
||||
{{'attachments' | i18n}}
|
||||
<small>{{cipher.name}}</small>
|
||||
</h2>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="box" *ngIf="cipher && cipher.hasAttachments">
|
||||
<div class="box-header">
|
||||
{{'attachments' | i18n}}
|
||||
</div>
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let a of cipher.attachments">
|
||||
<div class="row-main">
|
||||
<table class="table table-hover table-list" *ngIf="cipher && cipher.hasAttachments">
|
||||
<tbody>
|
||||
<tr *ngFor="let a of cipher.attachments">
|
||||
<td>
|
||||
{{a.fileName}}
|
||||
</div>
|
||||
<small class="row-sub-label">{{a.sizeName}}</small>
|
||||
<div class="action-buttons no-pad">
|
||||
<button class="row-btn btn" type="button" appStopClick appBlurClick
|
||||
title="{{'delete' | i18n}}" (click)="delete(a)" #deleteBtn
|
||||
[appApiAction]="deletePromises[a.id]" [disabled]="deleteBtn.loading">
|
||||
<br>
|
||||
<small>{{a.sizeName}}</small>
|
||||
</td>
|
||||
<td class="table-list-options">
|
||||
<button class="btn btn-outline-danger" type="button" appStopClick appBlurClick title="{{'delete' | i18n}}" (click)="delete(a)"
|
||||
#deleteBtn [appApiAction]="deletePromises[a.id]" [disabled]="deleteBtn.loading">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'newAttachment' | i18n}}
|
||||
</div>
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row">
|
||||
<label for="file">{{'file' | i18n}}</label>
|
||||
<input type="file" id="file" name="file" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'maxFileSize' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>{{'newAttachment' | i18n}}</h3>
|
||||
<label for="file" class="sr-only">{{'file' | i18n}}</label>
|
||||
<input type="file" id="file" class="form-control-file" name="file" required>
|
||||
<small class="form-text text-muted">{{'maxFileSize' | i18n}}</small>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" title="{{'save' | i18n}}" [disabled]="form.loading">
|
||||
<button appBlurClick type="submit" class="btn btn-primary" title="{{'save' | i18n}}" [disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal" title="{{'close' | i18n}}">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<ng-container *ngIf="(ciphers | searchCiphers: searchText) as searchedCiphers">
|
||||
<table class="table table-hover" *ngIf="searchedCiphers.length > 0">
|
||||
<table class="table table-hover table-list table-ciphers" *ngIf="searchedCiphers.length > 0">
|
||||
<tbody>
|
||||
<tr *ngFor="let c of searchedCiphers">
|
||||
<td (click)="checkCipher(c)">
|
||||
@ -15,30 +15,30 @@
|
||||
<br>
|
||||
<small appStopProp>{{c.subTitle}}</small>
|
||||
</td>
|
||||
<td>
|
||||
<td class="table-list-options">
|
||||
<div class="dropdown" appListDropdown>
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<i class="fa fa-cog"></i>
|
||||
<i class="fa fa-cog fa-lg"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="c.type === cipherType.Login">
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="c.type === cipherType.Login" (click)="copy(c.login.password, 'password', 'password')">
|
||||
<i class="fa fa-fw fa-clipboard"></i>
|
||||
{{'copyPassword' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" appStopClick>
|
||||
<a class="dropdown-item" href="#" appStopClick (click)="attachments(c)">
|
||||
<i class="fa fa-fw fa-paperclip"></i>
|
||||
{{'attachments' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="!c.organizationId">
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="!c.organizationId" (click)="share(c)">
|
||||
<i class="fa fa-fw fa-share-alt"></i>
|
||||
{{'share' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="c.organizationId">
|
||||
<a class="dropdown-item" href="#" appStopClick *ngIf="c.organizationId" (click)="collections(c)">
|
||||
<i class="fa fa-fw fa-cubes"></i>
|
||||
{{'collections' | i18n}}
|
||||
</a>
|
||||
<a class="dropdown-item text-danger" href="#" appStopClick>
|
||||
<a class="dropdown-item text-danger" href="#" appStopClick (click)="delete(c)">
|
||||
<i class="fa fa-fw fa-trash-o"></i>
|
||||
{{'delete' | i18n}}
|
||||
</a>
|
||||
|
@ -1,6 +1,15 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { CiphersComponent as BaseCiphersComponent } from 'jslib/angular/components/ciphers.component';
|
||||
|
||||
@ -13,13 +22,43 @@ import { CipherView } from 'jslib/models/view/cipherView';
|
||||
templateUrl: 'ciphers.component.html',
|
||||
})
|
||||
export class CiphersComponent extends BaseCiphersComponent {
|
||||
@Output() onAttachmentsClicked = new EventEmitter<CipherView>();
|
||||
cipherType = CipherType;
|
||||
|
||||
constructor(cipherService: CipherService) {
|
||||
constructor(cipherService: CipherService, private analytics: Angulartics2,
|
||||
private toasterService: ToasterService, private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService) {
|
||||
super(cipherService);
|
||||
}
|
||||
|
||||
checkCipher(c: CipherView) {
|
||||
(c as any).checked = !(c as any).checked;
|
||||
}
|
||||
|
||||
attachments(c: CipherView) {
|
||||
this.onAttachmentsClicked.emit(c);
|
||||
}
|
||||
|
||||
share(c: CipherView) {
|
||||
//
|
||||
}
|
||||
|
||||
collections(c: CipherView) {
|
||||
//
|
||||
}
|
||||
|
||||
delete(c: CipherView) {
|
||||
//
|
||||
}
|
||||
|
||||
copy(value: string, typeI18nKey: string, aType: string) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.eventTrack.next({ action: 'Copied ' + aType.toLowerCase() + ' from listing.' });
|
||||
this.platformUtilsService.copyToClipboard(value, { doc: window.document });
|
||||
this.toasterService.popAsync('info', null,
|
||||
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-ciphers (onCipherClicked)="editCipher($event)">
|
||||
<app-vault-ciphers (onCipherClicked)="editCipher($event)" (onAttachmentsClicked)="editCipherAttachments($event)">
|
||||
</app-vault-ciphers>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
|
@ -205,10 +205,10 @@ export class VaultComponent implements OnInit {
|
||||
AddEditComponent, this.cipherAddEditRef);
|
||||
|
||||
childComponent.cipherId = cipher == null ? null : cipher.id;
|
||||
childComponent.onSavedCipher.subscribe(async (cipher: CipherView) => {
|
||||
childComponent.onSavedCipher.subscribe(async (c: CipherView) => {
|
||||
this.modal.close();
|
||||
});
|
||||
childComponent.onDeletedCipher.subscribe(async (cipher: CipherView) => {
|
||||
childComponent.onDeletedCipher.subscribe(async (c: CipherView) => {
|
||||
this.modal.close();
|
||||
});
|
||||
|
||||
|
@ -385,5 +385,29 @@
|
||||
},
|
||||
"launch": {
|
||||
"message": "Launch"
|
||||
},
|
||||
"newAttachment": {
|
||||
"message": "Add New Attachment"
|
||||
},
|
||||
"deletedAttachment": {
|
||||
"message": "Deleted attachment"
|
||||
},
|
||||
"deleteAttachmentConfirmation": {
|
||||
"message": "Are you sure you want to delete this attachment?"
|
||||
},
|
||||
"attachmentSaved": {
|
||||
"message": "The attachment has been saved."
|
||||
},
|
||||
"file": {
|
||||
"message": "File"
|
||||
},
|
||||
"selectFile": {
|
||||
"message": "Select a file."
|
||||
},
|
||||
"maxFileSize": {
|
||||
"message": "Maximum file size is 100 MB."
|
||||
},
|
||||
"updateKey": {
|
||||
"message": "You cannot use this feature until you update your encryption key."
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ $input-bg: #fafafa;
|
||||
$input-focus-bg: #ffffff;
|
||||
$input-disabled-bg: #e0e0e0;
|
||||
|
||||
$table-accent-bg: rgba(#000000, .02);
|
||||
$table-hover-bg: rgba(#000000, .03);
|
||||
|
||||
@import "../../node_modules/bootstrap/scss/bootstrap";
|
||||
@ -143,32 +144,57 @@ form label {
|
||||
}
|
||||
}
|
||||
|
||||
.table.table-list {
|
||||
tr:first-child {
|
||||
td {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
td.table-list-options > .dropdown button, td.table-list-options > button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
|
||||
small {
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
td.table-list-options {
|
||||
width: 76px;
|
||||
max-width: 76px;
|
||||
text-align: right;
|
||||
|
||||
.btn {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
line-height: $line-height-base;
|
||||
}
|
||||
|
||||
> .dropdown:not(.show) button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
> button {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-vault-icon .fa {
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
app-vault {
|
||||
.table {
|
||||
tr:first-child {
|
||||
td {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
td:last-child .dropdown button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
|
||||
small {
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.table-ciphers {
|
||||
td:first-child {
|
||||
width: 35px;
|
||||
max-width: 35px;
|
||||
@ -184,24 +210,6 @@ app-vault {
|
||||
@extend .img-fluid;
|
||||
}
|
||||
}
|
||||
|
||||
td:nth-child(3) {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
width: 72px;
|
||||
max-width: 72px;
|
||||
text-align: right;
|
||||
|
||||
.btn {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.dropdown:not(.show) button {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user