mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-27 03:11:35 +01:00
cipher listings in vault work
This commit is contained in:
parent
c35d60025c
commit
dd49b34024
@ -40,6 +40,7 @@ import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
||||
import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
||||
|
||||
import { ActionButtonsComponent } from './components/action-buttons.component';
|
||||
import { CiphersListComponent } from './components/ciphers-list.component';
|
||||
import { PopOutComponent } from './components/pop-out.component';
|
||||
|
||||
import { IconComponent } from 'jslib/angular/components/icon.component';
|
||||
@ -66,6 +67,7 @@ import { IconComponent } from 'jslib/angular/components/icon.component';
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CiphersComponent,
|
||||
CiphersListComponent,
|
||||
CurrentTabComponent,
|
||||
EnvironmentComponent,
|
||||
FallbackSrcDirective,
|
||||
|
13
src/popup2/components/ciphers-list.component.html
Normal file
13
src/popup2/components/ciphers-list.component.html
Normal file
@ -0,0 +1,13 @@
|
||||
<a *ngFor="let c of ciphers" (click)="selectCipher(c)" href="#" appStopClick title="{{title}}"
|
||||
class="box-content-row box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
<span class="text">
|
||||
{{c.name}}
|
||||
<i class="fa fa-share-alt text-muted" *ngIf="c.organizationId" title="{{'shared' | i18n}}"></i>
|
||||
<i class="fa fa-paperclip text-muted" *ngIf="c.hasAttachments" title="{{'attachments' | i18n}}"></i>
|
||||
</span>
|
||||
<span class="detail">{{c.subTitle}}</span>
|
||||
</div>
|
||||
<app-action-buttons [cipher]="c" class="action-buttons"></app-action-buttons>
|
||||
</a>
|
40
src/popup2/components/ciphers-list.component.ts
Normal file
40
src/popup2/components/ciphers-list.component.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import * as template from './ciphers-list.component.html';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
|
||||
import { CipherType } from 'jslib/enums/cipherType';
|
||||
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ciphers-list',
|
||||
template: template,
|
||||
})
|
||||
export class CiphersListComponent {
|
||||
@Output() onSelected = new EventEmitter<CipherView>();
|
||||
@Input() ciphers: CipherView[];
|
||||
@Input() showView: boolean = false;
|
||||
@Input() title: string;
|
||||
|
||||
cipherType = CipherType;
|
||||
|
||||
constructor() { }
|
||||
|
||||
selectCipher(c: CipherView) {
|
||||
this.onSelected.emit(c);
|
||||
}
|
||||
}
|
@ -16,26 +16,12 @@
|
||||
</header>
|
||||
<content>
|
||||
<ng-container *ngIf="(ciphers | searchCiphers: searchText) as searchedCiphers">
|
||||
<div class="box list" *ngIf="searchedCiphers.length > 0">
|
||||
<div class="box list only-list" *ngIf="searchedCiphers.length > 0">
|
||||
<div class="box-header">
|
||||
Some name here
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a *ngFor="let c of searchedCiphers" appStopClick (click)="selectCipher(c)"
|
||||
href="#" title="{{'viewItem' | i18n}}" class="box-content-row box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
<span class="text">
|
||||
{{c.name}}
|
||||
<i class="fa fa-share-alt text-muted" *ngIf="c.organizationId"
|
||||
title="{{'shared' | i18n}}"></i>
|
||||
<i class="fa fa-paperclip text-muted" *ngIf="c.hasAttachments"
|
||||
title="{{'attachments' | i18n}}"></i>
|
||||
</span>
|
||||
<span class="detail">{{c.subTitle}}</span>
|
||||
</div>
|
||||
<app-action-buttons [cipher]="c" class="action-buttons"></app-action-buttons>
|
||||
</a>
|
||||
<app-ciphers-list [ciphers]="searchedCiphers" title="{{'viewItem' | i18n}}"></app-ciphers-list>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!searchedCiphers.length">
|
||||
|
@ -6,68 +6,107 @@
|
||||
<span class="title">{{'myVault' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
|
||||
<button appBlurClick (click)="addCipher()" title="{{'addItem' | i18n}}">
|
||||
<i class="fa fa-plus fa-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'favorites' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a *ngFor="let c of favoriteCiphers" href="#" class="box-content-row" appStopClick appBlurClick>
|
||||
{{c.name}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'types' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
||||
{{'typeLogin' | i18n}}
|
||||
<span>{{typeCounts.get(cipherType.Login) || 0}}</span>
|
||||
</a>
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
||||
{{'typeCard' | i18n}}
|
||||
<span>{{typeCounts.get(cipherType.Card) || 0}}</span>
|
||||
</a>
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
||||
{{'typeIdentity' | i18n}}
|
||||
<span>{{typeCounts.get(cipherType.Identity) || 0}}</span>
|
||||
</a>
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
||||
{{'typeSecureNote' | i18n}}
|
||||
<span>{{typeCounts.get(cipherType.SecureNote) || 0}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p *ngIf="!loaded" class="text-muted">{{'loading' | i18n}}</p>
|
||||
<ng-container *ngIf="loaded">
|
||||
<div class="box">
|
||||
<div class="box list" *ngIf="favoriteCiphers">
|
||||
<div class="box-header">
|
||||
{{'folders' | i18n}}
|
||||
{{'favorites' | i18n}}
|
||||
<span class="flex-right">{{favoriteCiphers.length}}</span>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a *ngFor="let f of folders" href="#" class="box-content-row" appStopClick appBlurClick
|
||||
(click)="selectFolder(f)">
|
||||
{{f.name}}
|
||||
<span>{{folderCounts.get(f.id) || 0}}</span>
|
||||
<app-ciphers-list [ciphers]="favoriteCiphers" title="{{'viewItem' | i18n}}"></app-ciphers-list>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<div class="box-header">
|
||||
{{'types' | i18n}}
|
||||
<span class="flex-right">4</span>
|
||||
</div>
|
||||
<div class="box-content single-line">
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-globe"></i></div>
|
||||
{{'typeLogin' | i18n}}
|
||||
<div class="flex-right">
|
||||
<span class="row-sub-label">{{typeCounts.get(cipherType.Login) || 0}}</span>
|
||||
<i class="fa fa-chevron-right fa-lg"></i>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-credit-card"></i></div>
|
||||
{{'typeCard' | i18n}}
|
||||
<div class="flex-right">
|
||||
<span class="row-sub-label">{{typeCounts.get(cipherType.Card) || 0}}</span>
|
||||
<i class="fa fa-chevron-right fa-lg"></i>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-id-card-o"></i></div>
|
||||
{{'typeIdentity' | i18n}}
|
||||
<div class="flex-right">
|
||||
<span class="row-sub-label">{{typeCounts.get(cipherType.Identity) || 0}}</span>
|
||||
<i class="fa fa-chevron-right fa-lg"></i>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="box-content-row" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-sticky-note-o"></i></div>
|
||||
{{'typeSecureNote' | i18n}}
|
||||
<div class="flex-right">
|
||||
<span class="row-sub-label">{{typeCounts.get(cipherType.SecureNote) || 0}}</span>
|
||||
<i class="fa fa-chevron-right fa-lg"></i>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box list">
|
||||
<div class="box-header">
|
||||
{{'folders' | i18n}}
|
||||
<span class="flex-right">{{folderCount}}</span>
|
||||
</div>
|
||||
<div class="box-content single-line">
|
||||
<a *ngFor="let f of folders" href="#" class="box-content-row" appStopClick appBlurClick
|
||||
(click)="selectFolder(f)">
|
||||
<div class="icon">
|
||||
<i class="fa fa-fw fa-lg" [ngClass]="{'fa-folder-open': f.id, 'fa-folder-open-o': !f.id}"></i>
|
||||
</div>
|
||||
{{f.name}}
|
||||
<div class="flex-right">
|
||||
<span class="row-sub-label">{{folderCounts.get(f.id) || 0}}</span>
|
||||
<i class="fa fa-chevron-right fa-lg"></i>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<div class="box-header">
|
||||
{{'collections' | i18n}}
|
||||
<span class="flex-right">{{collections.length}}</span>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content single-line">
|
||||
<a *ngFor="let c of collections" href="#" class="box-content-row" appStopClick appBlurClick
|
||||
(click)="selectCollection(c)">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-cube"></i></div>
|
||||
{{c.name}}
|
||||
<span>{{collectionCounts.get(c.id) || 0}}</span>
|
||||
<div class="flex-right">
|
||||
<span class="row-sub-label">{{collectionCounts.get(c.id) || 0}}</span>
|
||||
<i class="fa fa-chevron-right fa-lg"></i>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list" *ngIf="showNoFolderCiphers">
|
||||
<div class="box-header">
|
||||
{{'noneFolder' | i18n}}
|
||||
<div class="flex-right">{{noFolderCiphers.length}}</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<app-ciphers-list [ciphers]="noFolderCiphers" title="{{'viewItem' | i18n}}"></app-ciphers-list>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</content>
|
||||
|
@ -25,9 +25,12 @@ import { GroupingsComponent as BaseGroupingsComponent } from 'jslib/angular/comp
|
||||
export class GroupingsComponent extends BaseGroupingsComponent implements OnInit {
|
||||
ciphers: CipherView[];
|
||||
favoriteCiphers: CipherView[];
|
||||
noFolderCiphers: CipherView[];
|
||||
folderCount: number;
|
||||
folderCounts = new Map<string, number>();
|
||||
collectionCounts = new Map<string, number>();
|
||||
typeCounts = new Map<CipherType, number>();
|
||||
showNoFolderCiphers = false;
|
||||
|
||||
constructor(collectionService: CollectionService, folderService: FolderService,
|
||||
private cipherService: CipherService, private router: Router) {
|
||||
@ -35,9 +38,20 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
super.load();
|
||||
await super.load();
|
||||
super.loaded = false;
|
||||
|
||||
await this.loadCiphers();
|
||||
this.showNoFolderCiphers = this.noFolderCiphers != null && this.noFolderCiphers.length < 100 &&
|
||||
this.collections.length === 0;
|
||||
if (this.showNoFolderCiphers) {
|
||||
// Remove "No Folder" from folder listing
|
||||
this.folders.pop();
|
||||
this.folderCount = this.folders.length;
|
||||
} else {
|
||||
this.folderCount = this.folders.length - 1;
|
||||
}
|
||||
|
||||
super.loaded = true;
|
||||
}
|
||||
|
||||
@ -51,6 +65,13 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
||||
this.favoriteCiphers.push(c);
|
||||
}
|
||||
|
||||
if (c.folderId == null) {
|
||||
if (this.noFolderCiphers == null) {
|
||||
this.noFolderCiphers = [];
|
||||
}
|
||||
this.noFolderCiphers.push(c);
|
||||
}
|
||||
|
||||
if (this.typeCounts.has(c.type)) {
|
||||
this.typeCounts.set(c.type, this.typeCounts.get(c.type) + 1);
|
||||
} else {
|
||||
|
@ -12,6 +12,7 @@
|
||||
margin: 0 10px 5px 10px;
|
||||
color: $gray-light;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.box-content {
|
||||
@ -227,7 +228,7 @@
|
||||
width: calc(100% - 25px);
|
||||
}
|
||||
|
||||
.row-sub-icon {
|
||||
.row-sub-icon, .row-sub-label + i.fa {
|
||||
color: $list-icon-color;
|
||||
}
|
||||
|
||||
@ -236,6 +237,23 @@
|
||||
color: $gray-light;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
float: left;
|
||||
height: 36px;
|
||||
width: 34px;
|
||||
margin-left: -5px;
|
||||
color: $text-muted;
|
||||
|
||||
img {
|
||||
border-radius: $border-radius;
|
||||
max-height: 20px;
|
||||
max-width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.condensed .box-content-row, .box-content-row.condensed {
|
||||
@ -248,6 +266,17 @@
|
||||
background-color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&.single-line {
|
||||
.box-content-row {
|
||||
display: flex;
|
||||
|
||||
.icon {
|
||||
height: initial;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-footer {
|
||||
@ -257,8 +286,6 @@
|
||||
}
|
||||
|
||||
&.list {
|
||||
margin-bottom: 0;
|
||||
|
||||
.box-content {
|
||||
.box-content-row {
|
||||
padding: 3px 10px;
|
||||
@ -274,36 +301,34 @@
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
.row-btn {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.text, .detail {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: $font-size-small;
|
||||
color: $gray-light;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
float: left;
|
||||
height: 36px;
|
||||
width: 34px;
|
||||
margin-left: -5px;
|
||||
color: $text-muted;
|
||||
|
||||
img {
|
||||
border-radius: $border-radius;
|
||||
max-height: 20px;
|
||||
max-width: 20px;
|
||||
}
|
||||
&.single-line {
|
||||
.box-content-row {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.only-list {
|
||||
margin-bottom: 0;
|
||||
|
||||
.box-content {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,10 @@ small {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flex-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user