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 { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
||||||
|
|
||||||
import { ActionButtonsComponent } from './components/action-buttons.component';
|
import { ActionButtonsComponent } from './components/action-buttons.component';
|
||||||
|
import { CiphersListComponent } from './components/ciphers-list.component';
|
||||||
import { PopOutComponent } from './components/pop-out.component';
|
import { PopOutComponent } from './components/pop-out.component';
|
||||||
|
|
||||||
import { IconComponent } from 'jslib/angular/components/icon.component';
|
import { IconComponent } from 'jslib/angular/components/icon.component';
|
||||||
@ -66,6 +67,7 @@ import { IconComponent } from 'jslib/angular/components/icon.component';
|
|||||||
BlurClickDirective,
|
BlurClickDirective,
|
||||||
BoxRowDirective,
|
BoxRowDirective,
|
||||||
CiphersComponent,
|
CiphersComponent,
|
||||||
|
CiphersListComponent,
|
||||||
CurrentTabComponent,
|
CurrentTabComponent,
|
||||||
EnvironmentComponent,
|
EnvironmentComponent,
|
||||||
FallbackSrcDirective,
|
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>
|
</header>
|
||||||
<content>
|
<content>
|
||||||
<ng-container *ngIf="(ciphers | searchCiphers: searchText) as searchedCiphers">
|
<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">
|
<div class="box-header">
|
||||||
Some name here
|
Some name here
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<a *ngFor="let c of searchedCiphers" appStopClick (click)="selectCipher(c)"
|
<app-ciphers-list [ciphers]="searchedCiphers" title="{{'viewItem' | i18n}}"></app-ciphers-list>
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="no-items" *ngIf="!searchedCiphers.length">
|
<div class="no-items" *ngIf="!searchedCiphers.length">
|
||||||
|
@ -6,68 +6,107 @@
|
|||||||
<span class="title">{{'myVault' | i18n}}</span>
|
<span class="title">{{'myVault' | i18n}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
|
<button appBlurClick (click)="addCipher()" title="{{'addItem' | i18n}}">
|
||||||
|
<i class="fa fa-plus fa-lg"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<content>
|
<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>
|
<p *ngIf="!loaded" class="text-muted">{{'loading' | i18n}}</p>
|
||||||
<ng-container *ngIf="loaded">
|
<ng-container *ngIf="loaded">
|
||||||
<div class="box">
|
<div class="box list" *ngIf="favoriteCiphers">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'folders' | i18n}}
|
{{'favorites' | i18n}}
|
||||||
|
<span class="flex-right">{{favoriteCiphers.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<a *ngFor="let f of folders" href="#" class="box-content-row" appStopClick appBlurClick
|
<app-ciphers-list [ciphers]="favoriteCiphers" title="{{'viewItem' | i18n}}"></app-ciphers-list>
|
||||||
(click)="selectFolder(f)">
|
</div>
|
||||||
{{f.name}}
|
</div>
|
||||||
<span>{{folderCounts.get(f.id) || 0}}</span>
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="box-header">
|
||||||
{{'collections' | i18n}}
|
{{'collections' | i18n}}
|
||||||
|
<span class="flex-right">{{collections.length}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content single-line">
|
||||||
<a *ngFor="let c of collections" href="#" class="box-content-row" appStopClick appBlurClick
|
<a *ngFor="let c of collections" href="#" class="box-content-row" appStopClick appBlurClick
|
||||||
(click)="selectCollection(c)">
|
(click)="selectCollection(c)">
|
||||||
|
<div class="icon"><i class="fa fa-fw fa-lg fa-cube"></i></div>
|
||||||
{{c.name}}
|
{{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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</ng-container>
|
||||||
</content>
|
</content>
|
||||||
|
@ -25,9 +25,12 @@ import { GroupingsComponent as BaseGroupingsComponent } from 'jslib/angular/comp
|
|||||||
export class GroupingsComponent extends BaseGroupingsComponent implements OnInit {
|
export class GroupingsComponent extends BaseGroupingsComponent implements OnInit {
|
||||||
ciphers: CipherView[];
|
ciphers: CipherView[];
|
||||||
favoriteCiphers: CipherView[];
|
favoriteCiphers: CipherView[];
|
||||||
|
noFolderCiphers: CipherView[];
|
||||||
|
folderCount: number;
|
||||||
folderCounts = new Map<string, number>();
|
folderCounts = new Map<string, number>();
|
||||||
collectionCounts = new Map<string, number>();
|
collectionCounts = new Map<string, number>();
|
||||||
typeCounts = new Map<CipherType, number>();
|
typeCounts = new Map<CipherType, number>();
|
||||||
|
showNoFolderCiphers = false;
|
||||||
|
|
||||||
constructor(collectionService: CollectionService, folderService: FolderService,
|
constructor(collectionService: CollectionService, folderService: FolderService,
|
||||||
private cipherService: CipherService, private router: Router) {
|
private cipherService: CipherService, private router: Router) {
|
||||||
@ -35,9 +38,20 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
super.load();
|
await super.load();
|
||||||
super.loaded = false;
|
super.loaded = false;
|
||||||
|
|
||||||
await this.loadCiphers();
|
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;
|
super.loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +65,13 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
|||||||
this.favoriteCiphers.push(c);
|
this.favoriteCiphers.push(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c.folderId == null) {
|
||||||
|
if (this.noFolderCiphers == null) {
|
||||||
|
this.noFolderCiphers = [];
|
||||||
|
}
|
||||||
|
this.noFolderCiphers.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.typeCounts.has(c.type)) {
|
if (this.typeCounts.has(c.type)) {
|
||||||
this.typeCounts.set(c.type, this.typeCounts.get(c.type) + 1);
|
this.typeCounts.set(c.type, this.typeCounts.get(c.type) + 1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
margin: 0 10px 5px 10px;
|
margin: 0 10px 5px 10px;
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-content {
|
.box-content {
|
||||||
@ -227,7 +228,7 @@
|
|||||||
width: calc(100% - 25px);
|
width: calc(100% - 25px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-sub-icon {
|
.row-sub-icon, .row-sub-label + i.fa {
|
||||||
color: $list-icon-color;
|
color: $list-icon-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +237,23 @@
|
|||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
white-space: nowrap;
|
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 {
|
&.condensed .box-content-row, .box-content-row.condensed {
|
||||||
@ -248,6 +266,17 @@
|
|||||||
background-color: initial;
|
background-color: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.single-line {
|
||||||
|
.box-content-row {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
height: initial;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-footer {
|
.box-footer {
|
||||||
@ -257,8 +286,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.list {
|
&.list {
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
.box-content {
|
.box-content {
|
||||||
.box-content-row {
|
.box-content-row {
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
@ -274,36 +301,34 @@
|
|||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
.row-btn {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.text, .detail {
|
.text, .detail {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
display: block;
|
|
||||||
color: $text-color;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.detail {
|
&.single-line {
|
||||||
font-size: $font-size-small;
|
.box-content-row {
|
||||||
color: $gray-light;
|
padding-top: 10px;
|
||||||
}
|
padding-bottom: 10px;
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.only-list {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.box-content {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ small {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-right {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.no-margin {
|
.no-margin {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user