mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-22 16:29:09 +01:00
cipher listing with action button and pop comps
This commit is contained in:
parent
fa4589c15f
commit
3a9a7d3e64
2
jslib
2
jslib
@ -1 +1 @@
|
|||||||
Subproject commit a0ca51dda4ebbf710284a0f4e59d47ac8f31c8e7
|
Subproject commit 22f0f97cda0286859ceb889b9c80b9b5bb88affa
|
@ -283,8 +283,8 @@
|
|||||||
"viewItem": {
|
"viewItem": {
|
||||||
"message": "View Item"
|
"message": "View Item"
|
||||||
},
|
},
|
||||||
"launchWebsite": {
|
"launch": {
|
||||||
"message": "Launch Website"
|
"message": "Launch"
|
||||||
},
|
},
|
||||||
"website": {
|
"website": {
|
||||||
"message": "Website"
|
"message": "Website"
|
||||||
@ -383,8 +383,14 @@
|
|||||||
"message": "Verification code is required."
|
"message": "Verification code is required."
|
||||||
},
|
},
|
||||||
"valueCopied": {
|
"valueCopied": {
|
||||||
"message": " copied",
|
"message": "$VALUE$ copied",
|
||||||
"description": "' copied'. This is part of a sentence so be sure to leave the space prefix. For example: 'Password copied'"
|
"description": "Value has been copied to the clipboard.",
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "Password"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"autofillError": {
|
"autofillError": {
|
||||||
"message": "Unable to auto-fill the selected item on this page. Copy and paste the information instead."
|
"message": "Unable to auto-fill the selected item on this page. Copy and paste the information instead."
|
||||||
|
@ -15,6 +15,7 @@ import { RegisterComponent } from './accounts/register.component';
|
|||||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||||
import { TabsComponent } from './tabs.component';
|
import { TabsComponent } from './tabs.component';
|
||||||
|
import { CiphersComponent } from './vault/ciphers.component';
|
||||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||||
import { GroupingsComponent } from './vault/groupings.component';
|
import { GroupingsComponent } from './vault/groupings.component';
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ const routes: Routes = [
|
|||||||
{ path: 'register', component: RegisterComponent },
|
{ path: 'register', component: RegisterComponent },
|
||||||
{ path: 'hint', component: HintComponent },
|
{ path: 'hint', component: HintComponent },
|
||||||
{ path: 'environment', component: EnvironmentComponent },
|
{ path: 'environment', component: EnvironmentComponent },
|
||||||
|
{ path: 'ciphers', component: CiphersComponent },
|
||||||
{
|
{
|
||||||
path: 'tabs', component: TabsComponent,
|
path: 'tabs', component: TabsComponent,
|
||||||
children: [
|
children: [
|
||||||
|
@ -24,6 +24,7 @@ import { RegisterComponent } from './accounts/register.component';
|
|||||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||||
import { TabsComponent } from './tabs.component';
|
import { TabsComponent } from './tabs.component';
|
||||||
|
import { CiphersComponent } from './vault/ciphers.component';
|
||||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||||
import { GroupingsComponent } from './vault/groupings.component';
|
import { GroupingsComponent } from './vault/groupings.component';
|
||||||
|
|
||||||
@ -36,6 +37,12 @@ import { StopClickDirective } from 'jslib/angular/directives/stop-click.directiv
|
|||||||
import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive';
|
import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive';
|
||||||
|
|
||||||
import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
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 { PopOutComponent } from './components/pop-out.component';
|
||||||
|
|
||||||
|
import { IconComponent } from 'jslib/angular/components/icon.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -52,11 +59,13 @@ import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
|||||||
ToasterModule,
|
ToasterModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
ActionButtonsComponent,
|
||||||
ApiActionDirective,
|
ApiActionDirective,
|
||||||
AppComponent,
|
AppComponent,
|
||||||
AutofocusDirective,
|
AutofocusDirective,
|
||||||
BlurClickDirective,
|
BlurClickDirective,
|
||||||
BoxRowDirective,
|
BoxRowDirective,
|
||||||
|
CiphersComponent,
|
||||||
CurrentTabComponent,
|
CurrentTabComponent,
|
||||||
EnvironmentComponent,
|
EnvironmentComponent,
|
||||||
FallbackSrcDirective,
|
FallbackSrcDirective,
|
||||||
@ -64,9 +73,12 @@ import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
|||||||
HomeComponent,
|
HomeComponent,
|
||||||
HintComponent,
|
HintComponent,
|
||||||
I18nPipe,
|
I18nPipe,
|
||||||
|
IconComponent,
|
||||||
LockComponent,
|
LockComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
|
PopOutComponent,
|
||||||
RegisterComponent,
|
RegisterComponent,
|
||||||
|
SearchCiphersPipe,
|
||||||
StopClickDirective,
|
StopClickDirective,
|
||||||
StopPropDirective,
|
StopPropDirective,
|
||||||
TabsComponent,
|
TabsComponent,
|
||||||
|
37
src/popup2/components/action-buttons.component.html
Normal file
37
src/popup2/components/action-buttons.component.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<span class="row-btn" (click)="view()" appStopClick appBlurClick title="{{'view' | i18n}}" *ngIf="showView">
|
||||||
|
<i class="fa fa-lg fa-eye"></i>
|
||||||
|
</span>
|
||||||
|
<ng-container *ngIf="cipher.type === cipherType.Login">
|
||||||
|
<span class="row-btn" appStopClick appBlurClick title="{{'launch' | i18n}}" (click)="launch()"
|
||||||
|
*ngIf="!showView" [ngClass]="{disabled: !cipher.login.canLaunch}">
|
||||||
|
<i class="fa fa-lg fa-share-square-o"></i>
|
||||||
|
</span>
|
||||||
|
<span class="row-btn" appStopClick appBlurClick title="{{'copyUsername' | i18n}}"
|
||||||
|
(click)="copy(cipher.login.username, 'username', 'Username')"
|
||||||
|
[ngClass]="{disabled: !cipher.login.username}">
|
||||||
|
<i class="fa fa-lg fa-user"></i>
|
||||||
|
</span>
|
||||||
|
<span class="row-btn" appStopClick appBlurClick title="{{'copyPassword' | i18n}}"
|
||||||
|
(click)="copy(cipher.login.password, 'password', 'Password')"
|
||||||
|
[ngClass]="{disabled: !cipher.login.password}">
|
||||||
|
<i class="fa fa-lg fa-key"></i>
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="cipher.type === cipherType.Card">
|
||||||
|
<span class="row-btn" appStopClick appBlurClick title="{{'copyNumber' | i18n}}"
|
||||||
|
(click)="copy(cipher.card.number, 'number', 'Card Number')"
|
||||||
|
[ngClass]="{disabled: !cipher.card.number}">
|
||||||
|
<i class="fa fa-lg fa-hashtag"></i>
|
||||||
|
</span>
|
||||||
|
<span class="row-btn" appStopClick appBlurClick title="{{'copySecurityCode' | i18n}}"
|
||||||
|
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')"
|
||||||
|
[ngClass]="{disabled: !cipher.card.code}">
|
||||||
|
<i class="fa fa-lg fa-key"></i>
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="cipher.type === cipherType.SecureNote">
|
||||||
|
<span class="row-btn" appStopClick appBlurClick title="{{'copyNote' | i18n}}"
|
||||||
|
(click)="copy(cipher.notes, 'note', 'Note')" [ngClass]="{disabled: !cipher.notes}">
|
||||||
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
64
src/popup2/components/action-buttons.component.ts
Normal file
64
src/popup2/components/action-buttons.component.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import * as template from './action-buttons.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-action-buttons',
|
||||||
|
template: template,
|
||||||
|
})
|
||||||
|
export class ActionButtonsComponent {
|
||||||
|
@Output() onView = new EventEmitter<CipherView>();
|
||||||
|
@Input() cipher: CipherView;
|
||||||
|
@Input() showView: boolean = false;
|
||||||
|
|
||||||
|
cipherType = CipherType;
|
||||||
|
|
||||||
|
constructor(private analytics: Angulartics2, private toasterService: ToasterService,
|
||||||
|
private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
||||||
|
private popupUtilsService: PopupUtilsService) { }
|
||||||
|
|
||||||
|
launch() {
|
||||||
|
if (this.cipher.type !== CipherType.Login || !this.cipher.login.canLaunch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.analytics.eventTrack.next({ action: 'Launched URI From Listing' });
|
||||||
|
BrowserApi.createNewTab(this.cipher.login.uri);
|
||||||
|
if (this.popupUtilsService.inPopup(window)) {
|
||||||
|
BrowserApi.closePopup(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(value: string, typeI18nKey: string, aType: string) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.analytics.eventTrack.next({ action: 'Copied ' + aType });
|
||||||
|
this.platformUtilsService.copyToClipboard(value);
|
||||||
|
this.toasterService.popAsync('info', null,
|
||||||
|
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
this.onView.emit(this.cipher);
|
||||||
|
}
|
||||||
|
}
|
3
src/popup2/components/pop-out.component.html
Normal file
3
src/popup2/components/pop-out.component.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<button (click)="expand()" title="{{'popOutNewWindow' | i18n}}">
|
||||||
|
<i class="fa fa-external-link fa-rotate-270 fa-lg"></i>
|
||||||
|
</button>
|
64
src/popup2/components/pop-out.component.ts
Normal file
64
src/popup2/components/pop-out.component.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import * as template from './pop-out.component.html';
|
||||||
|
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
|
import { BrowserApi } from '../../browser/browserApi';
|
||||||
|
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
|
||||||
|
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-pop-out',
|
||||||
|
template: template,
|
||||||
|
})
|
||||||
|
export class PopOutComponent {
|
||||||
|
constructor(private analytics: Angulartics2, private platformUtilsService: PlatformUtilsService,
|
||||||
|
private popupUtilsService: PopupUtilsService) { }
|
||||||
|
|
||||||
|
expand() {
|
||||||
|
this.analytics.eventTrack.next({ action: 'Pop Out Window' });
|
||||||
|
|
||||||
|
let href = window.location.href;
|
||||||
|
if (this.platformUtilsService.isEdge()) {
|
||||||
|
const popupIndex = href.indexOf('/popup/');
|
||||||
|
if (popupIndex > -1) {
|
||||||
|
href = href.substring(popupIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((typeof chrome !== 'undefined') && chrome.windows && chrome.windows.create) {
|
||||||
|
if (href.indexOf('?uilocation=') > -1) {
|
||||||
|
href = href.replace('uilocation=popup', 'uilocation=popout')
|
||||||
|
.replace('uilocation=tab', 'uilocation=popout')
|
||||||
|
.replace('uilocation=sidebar', 'uilocation=popout');
|
||||||
|
} else {
|
||||||
|
const hrefParts = href.split('#');
|
||||||
|
href = hrefParts[0] + '?uilocation=popout' + (hrefParts.length > 0 ? '#' + hrefParts[1] : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const bodyRect = document.querySelector('body').getBoundingClientRect();
|
||||||
|
chrome.windows.create({
|
||||||
|
url: href,
|
||||||
|
type: 'popup',
|
||||||
|
width: bodyRect.width + 60,
|
||||||
|
height: bodyRect.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.popupUtilsService.inPopup(window)) {
|
||||||
|
BrowserApi.closePopup(window);
|
||||||
|
}
|
||||||
|
} else if ((typeof chrome !== 'undefined') && chrome.tabs && chrome.tabs.create) {
|
||||||
|
href = href.replace('uilocation=popup', 'uilocation=tab')
|
||||||
|
.replace('uilocation=popout', 'uilocation=tab')
|
||||||
|
.replace('uilocation=sidebar', 'uilocation=tab');
|
||||||
|
chrome.tabs.create({
|
||||||
|
url: href,
|
||||||
|
});
|
||||||
|
} else if ((typeof safari !== 'undefined')) {
|
||||||
|
// Safari can't open popup in full page tab :(
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/popup2/services/popup-utils.service.ts
Normal file
21
src/popup2/services/popup-utils.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PopupUtilsService {
|
||||||
|
inSidebar(win: Window): boolean {
|
||||||
|
return win.location.search !== '' && win.location.search.indexOf('uilocation=sidebar') > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inTab(win: Window): boolean {
|
||||||
|
return win.location.search !== '' && win.location.search.indexOf('uilocation=tab') > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inPopout(win: Window): boolean {
|
||||||
|
return win.location.search !== '' && win.location.search.indexOf('uilocation=popout') > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inPopup(win: Window): boolean {
|
||||||
|
return win.location.search === '' || win.location.search.indexOf('uilocation=') === -1 ||
|
||||||
|
win.location.search.indexOf('uilocation=popup') > -1;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ import { MessagingService } from 'jslib/abstractions/messaging.service';
|
|||||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
import { SettingsService } from 'jslib/abstractions/settings.service';
|
import { SettingsService } from 'jslib/abstractions/settings.service';
|
||||||
|
import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service';
|
||||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||||
import { SyncService } from 'jslib/abstractions/sync.service';
|
import { SyncService } from 'jslib/abstractions/sync.service';
|
||||||
import { TokenService } from 'jslib/abstractions/token.service';
|
import { TokenService } from 'jslib/abstractions/token.service';
|
||||||
@ -33,11 +34,13 @@ import { UserService } from 'jslib/abstractions/user.service';
|
|||||||
import { UtilsService } from 'jslib/abstractions/utils.service';
|
import { UtilsService } from 'jslib/abstractions/utils.service';
|
||||||
|
|
||||||
import { AutofillService } from '../../services/abstractions/autofill.service';
|
import { AutofillService } from '../../services/abstractions/autofill.service';
|
||||||
|
|
||||||
import BrowserMessagingService from '../../services/browserMessaging.service';
|
import BrowserMessagingService from '../../services/browserMessaging.service';
|
||||||
|
|
||||||
import { AuthService } from 'jslib/services/auth.service';
|
import { AuthService } from 'jslib/services/auth.service';
|
||||||
import { ConstantsService } from 'jslib/services/constants.service';
|
import { ConstantsService } from 'jslib/services/constants.service';
|
||||||
|
import { StateService } from 'jslib/services/state.service';
|
||||||
|
|
||||||
|
import { PopupUtilsService } from './popup-utils.service';
|
||||||
|
|
||||||
function getBgService<T>(service: string) {
|
function getBgService<T>(service: string) {
|
||||||
return (): T => {
|
return (): T => {
|
||||||
@ -46,6 +49,7 @@ function getBgService<T>(service: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stateService = new StateService();
|
||||||
const messagingService = new BrowserMessagingService(getBgService<PlatformUtilsService>('platformUtilsService')());
|
const messagingService = new BrowserMessagingService(getBgService<PlatformUtilsService>('platformUtilsService')());
|
||||||
const authService = new AuthService(getBgService<CryptoService>('cryptoService')(),
|
const authService = new AuthService(getBgService<CryptoService>('cryptoService')(),
|
||||||
getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(),
|
getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(),
|
||||||
@ -53,11 +57,16 @@ const authService = new AuthService(getBgService<CryptoService>('cryptoService')
|
|||||||
getBgService<I18nService>('i18n2Service')(), getBgService<PlatformUtilsService>('platformUtilsService')(),
|
getBgService<I18nService>('i18n2Service')(), getBgService<PlatformUtilsService>('platformUtilsService')(),
|
||||||
getBgService<ConstantsService>('constantsService')(), messagingService);
|
getBgService<ConstantsService>('constantsService')(), messagingService);
|
||||||
|
|
||||||
function initFactory(): Function {
|
function initFactory(i18nService: I18nService, storageService: StorageService): Function {
|
||||||
return async () => {
|
return async () => {
|
||||||
if (getBgService<I18nService>('i18n2Service')() != null) {
|
const htmlEl = window.document.documentElement;
|
||||||
|
if (i18nService != null) {
|
||||||
authService.init();
|
authService.init();
|
||||||
|
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateService.save(ConstantsService.disableFaviconKey,
|
||||||
|
await storageService.get<boolean>(ConstantsService.disableFaviconKey));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,8 +78,10 @@ function initFactory(): Function {
|
|||||||
providers: [
|
providers: [
|
||||||
ValidationService,
|
ValidationService,
|
||||||
AuthGuardService,
|
AuthGuardService,
|
||||||
|
PopupUtilsService,
|
||||||
{ provide: MessagingService, useValue: messagingService },
|
{ provide: MessagingService, useValue: messagingService },
|
||||||
{ provide: AuthServiceAbstraction, useValue: authService },
|
{ provide: AuthServiceAbstraction, useValue: authService },
|
||||||
|
{ provide: StateServiceAbstraction, useValue: stateService },
|
||||||
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },
|
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },
|
||||||
{ provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] },
|
{ provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] },
|
||||||
{ provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] },
|
{ provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] },
|
||||||
@ -102,7 +113,7 @@ function initFactory(): Function {
|
|||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initFactory,
|
useFactory: initFactory,
|
||||||
deps: [],
|
deps: [I18nService, StorageService],
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
50
src/popup2/vault/ciphers.component.html
Normal file
50
src/popup2/vault/ciphers.component.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<header>
|
||||||
|
<div class="left">
|
||||||
|
<a routerLink="/tabs/vault">
|
||||||
|
<i class="fa fa-chevron-left"></i>
|
||||||
|
<span>{{'back' | i18n}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
{{searchPlaceholder || ('searchVault' | i18n)}}
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<button appBlurClick (click)="addCipher()" title="{{'addItem' | i18n}}">
|
||||||
|
<i class="fa fa-plus fa-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<content>
|
||||||
|
<ng-container *ngIf="(ciphers | searchCiphers: searchText) as searchedCiphers">
|
||||||
|
<div class="box 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="no-items" *ngIf="!searchedCiphers.length">
|
||||||
|
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded"></i>
|
||||||
|
<ng-container *ngIf="loaded">
|
||||||
|
<i class="fa fa-frown-o fa-4x"></i>
|
||||||
|
<p>{{'noItemsInList' | i18n}}</p>
|
||||||
|
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</content>
|
45
src/popup2/vault/ciphers.component.ts
Normal file
45
src/popup2/vault/ciphers.component.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import * as template from './ciphers.component.html';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
Router,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||||
|
|
||||||
|
import { CipherView } from 'jslib/models/view/cipherView';
|
||||||
|
|
||||||
|
import { CiphersComponent as BaseCiphersComponent } from 'jslib/angular/components/ciphers.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-vault-ciphers',
|
||||||
|
template: template,
|
||||||
|
})
|
||||||
|
export class CiphersComponent extends BaseCiphersComponent implements OnInit {
|
||||||
|
constructor(cipherService: CipherService, private route: ActivatedRoute) {
|
||||||
|
super(cipherService);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.route.queryParams.subscribe(async (params) => {
|
||||||
|
if (params.type) {
|
||||||
|
const t = parseInt(params.type, null);
|
||||||
|
await super.load((c) => c.type === t);
|
||||||
|
} else if (params.folderId) {
|
||||||
|
await super.load((c) => c.folderId === params.folderId);
|
||||||
|
} else if (params.collectionId) {
|
||||||
|
await super.load((c) => c.collectionIds.indexOf(params.collectionId) > -1);
|
||||||
|
} else {
|
||||||
|
await super.load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCipher(cipher: CipherView) {
|
||||||
|
super.selectCipher(cipher);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<header>
|
<header>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
|
<app-pop-out></app-pop-out>
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<span class="title">{{'myVault' | i18n}}</span>
|
<span class="title">{{'myVault' | i18n}}</span>
|
||||||
|
@ -4,10 +4,13 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { CipherType } from 'jslib/enums/cipherType';
|
import { CipherType } from 'jslib/enums/cipherType';
|
||||||
|
|
||||||
|
import { CollectionView } from 'jslib/models/view/collectionView';
|
||||||
import { CipherView } from 'jslib/models/view/cipherView';
|
import { CipherView } from 'jslib/models/view/cipherView';
|
||||||
|
import { FolderView } from 'jslib/models/view/folderView';
|
||||||
|
|
||||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||||
@ -27,15 +30,15 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
|||||||
typeCounts = new Map<CipherType, number>();
|
typeCounts = new Map<CipherType, number>();
|
||||||
|
|
||||||
constructor(collectionService: CollectionService, folderService: FolderService,
|
constructor(collectionService: CollectionService, folderService: FolderService,
|
||||||
private cipherService: CipherService) {
|
private cipherService: CipherService, private router: Router) {
|
||||||
super(collectionService, folderService);
|
super(collectionService, folderService);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.load();
|
super.load();
|
||||||
this.loaded = false;
|
super.loaded = false;
|
||||||
await this.loadCiphers();
|
await this.loadCiphers();
|
||||||
this.loaded = true;
|
super.loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadCiphers() {
|
async loadCiphers() {
|
||||||
@ -71,4 +74,19 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectType(type: CipherType) {
|
||||||
|
super.selectType(type);
|
||||||
|
this.router.navigate(['/ciphers', { queryParams: { type: type } }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectFolder(folder: FolderView) {
|
||||||
|
super.selectFolder(folder);
|
||||||
|
this.router.navigate(['/ciphers', { queryParams: { folderId: folder.id } }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCollection(collection: CollectionView) {
|
||||||
|
super.selectCollection(collection);
|
||||||
|
this.router.navigate(['/ciphers', { queryParams: { collectionId: collection.id } }]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,15 +113,16 @@ header {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
button, a {
|
app-pop-out > button, div > button, div > a {
|
||||||
background: $brand-primary;
|
background: $brand-primary;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
@ -131,6 +132,14 @@ header {
|
|||||||
&:focus {
|
&:focus {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i + span {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app-pop-out {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -255,4 +255,55 @@
|
|||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
color: $text-muted;
|
color: $text-muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.list {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.box-content {
|
||||||
|
.box-content-row {
|
||||||
|
padding: 3px 10px;
|
||||||
|
color: $text-color;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover, &:focus, &.active {
|
||||||
|
background-color: $list-item-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-left: 5px solid $text-muted;
|
||||||
|
padding-left: 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user