1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-24 12:06:15 +01:00

[Send] Type component (#1608)

* Initial commit of send type component

* Update jslib (ee164be -> 0951424)

* Update constructor for new jslib
This commit is contained in:
Vincent Salucci 2021-02-12 16:28:31 -06:00 committed by GitHub
parent 0d6e212463
commit 8d2e436a05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 240 additions and 14 deletions

2
jslib

@ -1 +1 @@
Subproject commit ee164bebc65aa56e41a122eb4ece8971eb23119b Subproject commit 0951424de77fbb61a38616d13d6c67f74ee19775

View File

@ -187,4 +187,7 @@ export const routerTransition = trigger('routerTransition', [
transition('premium => tabs', outSlideRight), transition('premium => tabs', outSlideRight),
transition('tabs => lock', inSlideDown), transition('tabs => lock', inSlideDown),
transition('tabs => send-type', inSlideLeft),
transition('send-type => tabs', outSlideRight),
]); ]);

View File

@ -47,6 +47,7 @@ import { ShareComponent } from './vault/share.component';
import { ViewComponent } from './vault/view.component'; import { ViewComponent } from './vault/view.component';
import { SendGroupingsComponent } from './send/send-groupings.component'; import { SendGroupingsComponent } from './send/send-groupings.component';
import { SendTypeComponent } from './send/send-type.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -236,6 +237,12 @@ const routes: Routes = [
canActivate: [AuthGuardService], canActivate: [AuthGuardService],
data: { state: 'clone-cipher' }, data: { state: 'clone-cipher' },
}, },
{
path: 'send-type',
component: SendTypeComponent,
canActivate: [AuthGuardService],
data: { state: 'send-type' },
},
{ {
path: 'tabs', path: 'tabs',
component: TabsComponent, component: TabsComponent,

View File

@ -146,9 +146,13 @@ export class AppComponent implements OnInit {
(window as any).previousPopupUrl.startsWith('/tabs/')) { (window as any).previousPopupUrl.startsWith('/tabs/')) {
this.stateService.remove('GroupingsComponent'); this.stateService.remove('GroupingsComponent');
this.stateService.remove('CiphersComponent'); this.stateService.remove('CiphersComponent');
this.stateService.remove('SendGroupingsComponent');
this.stateService.remove('SendGroupingsComponentScope');
this.stateService.remove('SendTypeComponent');
} }
if (url.startsWith('/tabs/')) { if (url.startsWith('/tabs/')) {
this.stateService.remove('addEditCipherInfo'); this.stateService.remove('addEditCipherInfo');
// TODO Remove any Send add/edit state information (?)
} }
(window as any).previousPopupUrl = url; (window as any).previousPopupUrl = url;

View File

@ -53,6 +53,7 @@ import { ShareComponent } from './vault/share.component';
import { ViewComponent } from './vault/view.component'; import { ViewComponent } from './vault/view.component';
import { SendGroupingsComponent } from './send/send-groupings.component'; import { SendGroupingsComponent } from './send/send-groupings.component';
import { SendTypeComponent } from './send/send-type.component';
import { A11yTitleDirective } from 'jslib/angular/directives/a11y-title.directive'; import { A11yTitleDirective } from 'jslib/angular/directives/a11y-title.directive';
import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive'; import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive';
@ -214,6 +215,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
SelectCopyDirective, SelectCopyDirective,
SendGroupingsComponent, SendGroupingsComponent,
SendListComponent, SendListComponent,
SendTypeComponent,
SettingsComponent, SettingsComponent,
ShareComponent, ShareComponent,
StopClickDirective, StopClickDirective,

View File

@ -11,7 +11,7 @@
<span class="text"> <span class="text">
{{s.name}} {{s.name}}
<ng-container *ngIf="s.disabled"> <ng-container *ngIf="s.disabled">
<i class="fa fa-ban text-muted" title="{{'disabled' | i18n}}" aria-hidden="true"></i> <i class="fa fa-warning text-muted" title="{{'disabled' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'disabled' | i18n}}</span> <span class="sr-only">{{'disabled' | i18n}}</span>
</ng-container> </ng-container>
<ng-container *ngIf="s.password"> <ng-container *ngIf="s.password">
@ -19,8 +19,7 @@
<span class="sr-only">{{'passwordProtected' | i18n}}</span> <span class="sr-only">{{'passwordProtected' | i18n}}</span>
</ng-container> </ng-container>
<ng-container *ngIf="s.maxAccessCountReached"> <ng-container *ngIf="s.maxAccessCountReached">
<i class="fa fa-warning text-muted" title="{{'maxAccessCountReached' | i18n}}" <i class="fa fa-ban text-muted" title="{{'maxAccessCountReached' | i18n}}" aria-hidden="true"></i>
aria-hidden="true"></i>
<span class="sr-only">{{'maxAccessCountReached' | i18n}}</span> <span class="sr-only">{{'maxAccessCountReached' | i18n}}</span>
</ng-container> </ng-container>
<ng-container *ngIf="s.expired"> <ng-container *ngIf="s.expired">

View File

@ -1,4 +1,5 @@
import { import {
ChangeDetectorRef,
Component, Component,
NgZone, NgZone,
} from '@angular/core'; } from '@angular/core';
@ -46,21 +47,24 @@ export class SendGroupingsComponent extends BaseSendComponent {
private loadedTimeout: number; private loadedTimeout: number;
constructor(sendService: SendService, i18nService: I18nService, constructor(sendService: SendService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService, platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService, ngZone: NgZone,
broadcasterService: BroadcasterService, ngZone: NgZone, policyService: PolicyService, policyService: PolicyService, userService: UserService, searchService: SearchService,
userService: UserService, searchService: SearchService,
private popupUtils: PopupUtilsService, private stateService: StateService, private popupUtils: PopupUtilsService, private stateService: StateService,
private route: ActivatedRoute, private router: Router, private syncService: SyncService) { private route: ActivatedRoute, private router: Router, private syncService: SyncService,
super(sendService, i18nService, platformUtilsService, environmentService, broadcasterService, ngZone, private changeDetectorRef: ChangeDetectorRef, private broadcasterService: BroadcasterService) {
searchService, policyService, userService); super(sendService, i18nService, platformUtilsService, environmentService, ngZone, searchService,
policyService, userService);
super.onSuccessfulLoad = async () => { super.onSuccessfulLoad = async () => {
this.calculateTypeCounts(); this.calculateTypeCounts();
this.selectAll();
}; };
} }
async ngOnInit() { async ngOnInit() {
// Determine Header details // Determine Header details
this.showLeftHeader = !(this.popupUtils.inSidebar(window) && this.platformUtilsService.isFirefox()); this.showLeftHeader = !(this.popupUtils.inSidebar(window) && this.platformUtilsService.isFirefox());
// Clear state of Send Type Component
this.stateService.remove('SendTypeComponent');
// Let super class finish // Let super class finish
await super.ngOnInit(); await super.ngOnInit();
// Handle State Restore if necessary // Handle State Restore if necessary
@ -83,6 +87,23 @@ export class SendGroupingsComponent extends BaseSendComponent {
if (!this.syncService.syncInProgress || restoredScopeState) { if (!this.syncService.syncInProgress || restoredScopeState) {
window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0); window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0);
} }
// Load all sends if sync completed in background
this.broadcasterService.subscribe(ComponentId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case 'syncCompleted':
window.setTimeout(() => {
this.load();
}, 500);
break;
default:
break;
}
this.changeDetectorRef.detectChanges();
});
});
} }
ngOnDestroy() { ngOnDestroy() {
@ -92,12 +113,12 @@ export class SendGroupingsComponent extends BaseSendComponent {
} }
// Save state // Save state
this.saveState(); this.saveState();
// Allow super to finish // Unsubscribe
super.ngOnDestroy(); this.broadcasterService.unsubscribe(ComponentId);
} }
async selectType(type: SendType) { async selectType(type: SendType) {
// TODO this.router.navigate(['/send-type-list'], { queryParams: { type: type } }); this.router.navigate(['/send-type'], { queryParams: { type: type } });
} }
async selectSend(s: SendView) { async selectSend(s: SendView) {
@ -115,7 +136,7 @@ export class SendGroupingsComponent extends BaseSendComponent {
private calculateTypeCounts() { private calculateTypeCounts() {
// Create type counts // Create type counts
const typeCounts = new Map<SendType, number>(); const typeCounts = new Map<SendType, number>();
this.sends.forEach((s) => { this.sends.forEach(s => {
if (typeCounts.has(s.type)) { if (typeCounts.has(s.type)) {
typeCounts.set(s.type, typeCounts.get(s.type) + 1); typeCounts.set(s.type, typeCounts.get(s.type) + 1);
} else { } else {

View File

@ -0,0 +1,41 @@
<header>
<div class="left">
<button type="button" appBlurClick (click)="back()">
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
<span>{{'back' | i18n}}</span>
</button>
</div>
<div class="search">
<input type="search" placeholder="{{'searchType' | i18n}}" id="search" [(ngModel)]="searchText"
(input)="search(200)" autocomplete="off" appAutofocus>
<i class="fa fa-search"></i>
</div>
<div class="right">
<button appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}">
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
</button>
</div>
</header>
<content>
<div class="no-items" *ngIf="!filteredSends.length">
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
<ng-container *ngIf="loaded">
<p>{{'noItemsInList' | i18n}}</p>
<button (click)="addSend()" class="btn block primary link">
{{'addSend' | i18n}}
</button>
</ng-container>
</div>
<div class="box list only-list" *ngIf="filteredSends.length">
<div class="box-header">
{{groupingTitle}}
<span class="flex-right">{{filteredSends.length}}</span>
</div>
<div class="box-content">
<app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" (onSelected)="selectSend($event)"
(onCopySendLink)="copy($event)" (onRemovePassword)="removePassword($event)"
(onDeleteSend)="delete($event)">
</app-send-list>
</div>
</div>
</content>

View File

@ -0,0 +1,149 @@
import {
ChangeDetectorRef,
Component,
NgZone,
} from '@angular/core';
import {
ActivatedRoute,
} from '@angular/router';
import { Location } from '@angular/common';
import { SendView } from 'jslib/models/view/sendView';
import { SendComponent as BaseSendComponent } from 'jslib/angular/components/send/send.component';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { PolicyService } from 'jslib/abstractions/policy.service';
import { SearchService } from 'jslib/abstractions/search.service';
import { SendService } from 'jslib/abstractions/send.service';
import { StateService } from 'jslib/abstractions/state.service';
import { UserService } from 'jslib/abstractions/user.service';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { PopupUtilsService } from '../services/popup-utils.service';
import { SendType } from 'jslib/enums/sendType';
const ComponentId = 'SendTypeComponent';
@Component({
selector: 'app-send-type',
templateUrl: 'send-type.component.html',
})
export class SendTypeComponent extends BaseSendComponent {
groupingTitle: string;
// State Handling
state: any;
private refreshTimeout: number;
private applySavedState = true;
constructor(sendService: SendService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService, ngZone: NgZone,
policyService: PolicyService, userService: UserService, searchService: SearchService,
private popupUtils: PopupUtilsService, private stateService: StateService,
private route: ActivatedRoute, private location: Location, private changeDetectorRef: ChangeDetectorRef,
private broadcasterService: BroadcasterService) {
super(sendService, i18nService, platformUtilsService, environmentService, ngZone, searchService,
policyService, userService);
super.onSuccessfulLoad = async () => {
this.selectType(this.type);
};
this.applySavedState = (window as any).previousPopupUrl != null &&
!(window as any).previousPopupUrl.startsWith('/send-type');
}
async ngOnInit() {
// Let super class finish
await super.ngOnInit();
const queryParamsSub = this.route.queryParams.subscribe(async params => {
if (this.applySavedState) {
this.state = (await this.stateService.get<any>(ComponentId)) || {};
if (this.state.searchText != null) {
this.searchText = this.state.searchText;
}
}
if (params.type != null) {
this.type = parseInt(params.type, null);
switch (this.type) {
case SendType.Text:
this.groupingTitle = this.i18nService.t('sendTypeText');
break;
case SendType.File:
this.groupingTitle = this.i18nService.t('sendTypeFile');
break;
default:
break;
}
await this.load(s => s.type === this.type);
}
// Restore state and remove reference
if (this.applySavedState && this.state != null) {
window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0);
}
this.stateService.remove(ComponentId);
// Unsubscribe
if (queryParamsSub != null) {
queryParamsSub.unsubscribe();
}
});
// Refresh Send list if sync completed in background
this.broadcasterService.subscribe(ComponentId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case 'syncCompleted':
if (message.successfully) {
this.refreshTimeout = window.setTimeout(() => {
this.refresh();
}, 500);
}
break;
default:
break;
}
this.changeDetectorRef.detectChanges();
});
});
}
ngOnDestroy() {
// Remove timeout
if (this.refreshTimeout != null) {
window.clearTimeout(this.refreshTimeout);
}
// Save state
this.saveState();
// Unsubscribe
this.broadcasterService.unsubscribe(ComponentId);
}
async selectSend(s: SendView) {
// TODO -> Route to edit send
}
async addSend() {
// TODO -> Route to create send
}
back() {
(window as any).routeDirection = 'b';
this.location.back();
}
private async saveState() {
this.state = {
scrollY: this.popupUtils.getContentScrollY(window),
searchText: this.searchText,
};
await this.stateService.save(ComponentId, this.state);
}
}