mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-28 03:21:40 +01:00
setup services, accounts components
This commit is contained in:
parent
28197970bd
commit
a4ff241574
2
jslib
2
jslib
@ -1 +1 @@
|
|||||||
Subproject commit 66b3dbae177a795d6b5168f7cdbba2f98f309272
|
Subproject commit 476d21e9f07f648784e92e6e7ec4ae37910e2449
|
5
locales/en/messages.json
Normal file
5
locales/en/messages.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"hello": {
|
||||||
|
"message": "hello world"
|
||||||
|
}
|
||||||
|
}
|
5
locales/es/messages.json
Normal file
5
locales/es/messages.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"hello": {
|
||||||
|
"message": "hola mundo"
|
||||||
|
}
|
||||||
|
}
|
30
src/app/accounts/hint.component.html
Normal file
30
src/app/accounts/hint.component.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
|
<header>
|
||||||
|
<div class="left">
|
||||||
|
<a routerLink="/login">{{'cancel' | i18n}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<span class="title">{{'passwordHint' | i18n}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||||
|
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
|
||||||
|
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<content>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||||
|
<input id="email" type="text" name="Email" [(ngModel)]="email" required appAutofocus
|
||||||
|
appInputVerbatim="false">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
{{'enterEmailToGetHint' | i18n}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
</form>
|
22
src/app/accounts/hint.component.ts
Normal file
22
src/app/accounts/hint.component.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { ToasterService } from 'angular2-toaster';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib/abstractions/api.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
|
||||||
|
import { HintComponent as BaseHintComponent } from 'jslib/angular/components/hint.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-hint',
|
||||||
|
templateUrl: 'hint.component.html',
|
||||||
|
})
|
||||||
|
export class HintComponent extends BaseHintComponent {
|
||||||
|
constructor(router: Router, analytics: Angulartics2,
|
||||||
|
toasterService: ToasterService, i18nService: I18nService,
|
||||||
|
apiService: ApiService) {
|
||||||
|
super(router, analytics, toasterService, i18nService, apiService);
|
||||||
|
}
|
||||||
|
}
|
40
src/app/accounts/login.component.html
Normal file
40
src/app/accounts/login.component.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<form id="login-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
|
<div class="content">
|
||||||
|
<p class="lead">{{'loginOrCreateNewAccount' | i18n}}</p>
|
||||||
|
<div class="box last">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||||
|
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
||||||
|
[appAutofocus]="email === ''">
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
|
<div class="row-main">
|
||||||
|
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||||
|
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||||
|
class="monospaced" [(ngModel)]="masterPassword" required [appAutofocus]="email !== ''">
|
||||||
|
</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||||
|
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||||
|
<i class="fa fa-lg"
|
||||||
|
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||||
|
<b [hidden]="form.loading"><i class="fa fa-sign-in"></i> {{'logIn' | i18n}}</b>
|
||||||
|
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading"></i>
|
||||||
|
</button>
|
||||||
|
<a routerLink="/register" class="btn block">
|
||||||
|
<i class="fa fa-pencil-square-o"></i> {{'createAccount' | i18n}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="sub-options">
|
||||||
|
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
24
src/app/accounts/login.component.ts
Normal file
24
src/app/accounts/login.component.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { ToasterService } from 'angular2-toaster';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
|
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { SyncService } from 'jslib/abstractions/sync.service';
|
||||||
|
|
||||||
|
import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/login.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
templateUrl: 'login.component.html',
|
||||||
|
})
|
||||||
|
export class LoginComponent extends BaseLoginComponent {
|
||||||
|
constructor(authService: AuthService, router: Router,
|
||||||
|
analytics: Angulartics2, toasterService: ToasterService,
|
||||||
|
i18nService: I18nService, private syncService: SyncService) {
|
||||||
|
super(authService, router, analytics, toasterService, i18nService);
|
||||||
|
this.successRoute = '/vault';
|
||||||
|
}
|
||||||
|
}
|
71
src/app/accounts/register.component.html
Normal file
71
src/app/accounts/register.component.html
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
|
<header>
|
||||||
|
<div class="left">
|
||||||
|
<a routerLink="/home">{{'cancel' | i18n}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<span class="title">{{'createAccount' | i18n}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||||
|
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
|
||||||
|
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<content>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||||
|
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
||||||
|
[appAutofocus]="email === ''" inputmode="email" appInputVerbatim="false">
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
|
<div class="row-main">
|
||||||
|
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||||
|
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||||
|
class="monospaced" [(ngModel)]="masterPassword" required [appAutofocus]="email !== ''"
|
||||||
|
appInputVerbatim>
|
||||||
|
</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||||
|
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||||
|
<i class="fa fa-lg"
|
||||||
|
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
{{'masterPassDesc' | i18n}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box last">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
|
<div class="row-main">
|
||||||
|
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||||
|
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
||||||
|
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="confirmMasterPassword"
|
||||||
|
required appInputVerbatim>
|
||||||
|
</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||||
|
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||||
|
<i class="fa fa-lg"
|
||||||
|
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||||
|
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
{{'masterPassHintDesc' | i18n}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
</form>
|
25
src/app/accounts/register.component.ts
Normal file
25
src/app/accounts/register.component.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { ToasterService } from 'angular2-toaster';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib/abstractions/api.service';
|
||||||
|
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||||
|
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
|
||||||
|
import { RegisterComponent as BaseRegisterComponent } from 'jslib/angular/components/register.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-register',
|
||||||
|
templateUrl: 'register.component.html',
|
||||||
|
})
|
||||||
|
export class RegisterComponent extends BaseRegisterComponent {
|
||||||
|
constructor(authService: AuthService, router: Router,
|
||||||
|
analytics: Angulartics2, toasterService: ToasterService,
|
||||||
|
i18nService: I18nService, cryptoService: CryptoService,
|
||||||
|
apiService: ApiService) {
|
||||||
|
super(authService, router, analytics, toasterService, i18nService, cryptoService, apiService);
|
||||||
|
}
|
||||||
|
}
|
24
src/app/accounts/two-factor-options.component.html
Normal file
24
src/app/accounts/two-factor-options.component.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<header>
|
||||||
|
<div class="left">
|
||||||
|
<a routerLink="/2fa">{{'close' | i18n}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<span class="title">{{'twoStepOptions' | i18n}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right"></div>
|
||||||
|
</header>
|
||||||
|
<content>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-content">
|
||||||
|
<a href="#" appStopClick *ngFor="let p of providers" class="box-content-row"
|
||||||
|
(click)="choose(p)">
|
||||||
|
<span class="text">{{p.name}}</span>
|
||||||
|
<span class="detail">{{p.description}}</span>
|
||||||
|
</a>
|
||||||
|
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
||||||
|
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
|
||||||
|
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</content>
|
31
src/app/accounts/two-factor-options.component.ts
Normal file
31
src/app/accounts/two-factor-options.component.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { ToasterService } from 'angular2-toaster';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
|
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
|
||||||
|
} from 'jslib/angular/components/two-factor-options.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-two-factor-options',
|
||||||
|
templateUrl: 'two-factor-options.component.html',
|
||||||
|
})
|
||||||
|
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||||
|
constructor(authService: AuthService, router: Router,
|
||||||
|
analytics: Angulartics2, toasterService: ToasterService,
|
||||||
|
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
|
||||||
|
super(authService, router, analytics, toasterService, i18nService, platformUtilsService, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
choose(p: any) {
|
||||||
|
super.choose(p);
|
||||||
|
this.authService.selectedTwoFactorProviderType = p.type;
|
||||||
|
this.router.navigate(['2fa']);
|
||||||
|
}
|
||||||
|
}
|
108
src/app/accounts/two-factor.component.html
Normal file
108
src/app/accounts/two-factor.component.html
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
|
<header>
|
||||||
|
<div class="left">
|
||||||
|
<a routerLink="/login">{{'back' | i18n}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<span class="title">{{title}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<button type="submit" appBlurClick [disabled]="form.loading"
|
||||||
|
*ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
|
||||||
|
selectedProviderType !== providerType.OrganizationDuo">
|
||||||
|
<span [hidden]="form.loading">{{'continue' | i18n}}</span>
|
||||||
|
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<content>
|
||||||
|
<ng-container *ngIf="selectedProviderType === providerType.Authenticator ||
|
||||||
|
selectedProviderType === providerType.Email">
|
||||||
|
<div class="content text-center">
|
||||||
|
<span *ngIf="selectedProviderType === providerType.Authenticator">
|
||||||
|
{{'enterVerificationCodeApp' | i18n}}
|
||||||
|
</span>
|
||||||
|
<span *ngIf="selectedProviderType === providerType.Email">
|
||||||
|
{{'enterVerificationCodeEmail' | i18n : twoFactorEmail}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="box first">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="code">{{'verificationCode' | i18n}}</label>
|
||||||
|
<input id="code" type="text" name="Code" [(ngModel)]="token" required appAutofocus
|
||||||
|
inputmode="tel" appInputVerbatim>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||||
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||||
|
<div class="content text-center">
|
||||||
|
<p>{{'insertYubiKey' | i18n}}</p>
|
||||||
|
<img src="../../images/two-factor/yubikey.jpg" class="img-rounded img-responsive" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="box first">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="code" class="sr-only">{{'verificationCode' | i18n}}</label>
|
||||||
|
<input id="code" type="password" name="Code" [(ngModel)]="token" required appAutofocus
|
||||||
|
appInputVerbatim>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||||
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="selectedProviderType === providerType.U2f">
|
||||||
|
<div class="content text-center">
|
||||||
|
<span *ngIf="!u2fReady"><i class="fa fa-spinner fa-spin"></i></span>
|
||||||
|
<div *ngIf="u2fReady">
|
||||||
|
<p>{{'insertU2f' | i18n}}</p>
|
||||||
|
<img src="../../images/two-factor/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box first">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||||
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
|
||||||
|
selectedProviderType === providerType.OrganizationDuo">
|
||||||
|
<div id="duo-frame" *ngIf="!showNewWindowMessage"><iframe id="duo_iframe"></iframe></div>
|
||||||
|
<div *ngIf="showNewWindowMessage" class="content text-center">{{'twoStepNewWindowMessage' | i18n}}</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||||
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<div class="content text-center" *ngIf="selectedProviderType == null">
|
||||||
|
<p>{{'noTwoStepProviders' | i18n}}</p>
|
||||||
|
<p>{{'noTwoStepProviders2' | i18n}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="content no-vpad text-center" *ngIf="selectedProviderType != null">
|
||||||
|
<p>
|
||||||
|
<a href="#" appStopClick (click)="anotherMethod()">{{'useAnotherTwoStepMethod' | i18n}}</a>
|
||||||
|
</p>
|
||||||
|
<p *ngIf="selectedProviderType === providerType.Email">
|
||||||
|
<a href="#" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise">
|
||||||
|
{{'sendVerificationCodeEmailAgain' | i18n}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</content>
|
||||||
|
</form>
|
||||||
|
<iframe id="u2f_iframe" hidden></iframe>
|
50
src/app/accounts/two-factor.component.ts
Normal file
50
src/app/accounts/two-factor.component.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
NgZone,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { ToasterService } from 'angular2-toaster';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
|
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib/abstractions/api.service';
|
||||||
|
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||||
|
import { EnvironmentService } from 'jslib/abstractions/environment.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
import { SyncService } from 'jslib/abstractions/sync.service';
|
||||||
|
|
||||||
|
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
||||||
|
|
||||||
|
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component';
|
||||||
|
|
||||||
|
const BroadcasterSubscriptionId = 'TwoFactorComponent';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-two-factor',
|
||||||
|
templateUrl: 'two-factor.component.html',
|
||||||
|
})
|
||||||
|
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||||
|
showNewWindowMessage = false;
|
||||||
|
|
||||||
|
constructor(authService: AuthService, router: Router,
|
||||||
|
analytics: Angulartics2, toasterService: ToasterService,
|
||||||
|
i18nService: I18nService, apiService: ApiService,
|
||||||
|
platformUtilsService: PlatformUtilsService, syncService: SyncService,
|
||||||
|
environmentService: EnvironmentService, private ngZone: NgZone,
|
||||||
|
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef) {
|
||||||
|
super(authService, router, analytics, toasterService, i18nService, apiService,
|
||||||
|
platformUtilsService, window, environmentService);
|
||||||
|
this.successRoute = '/vault';
|
||||||
|
}
|
||||||
|
|
||||||
|
anotherMethod() {
|
||||||
|
this.router.navigate(['2fa-options']);
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,19 @@ import {
|
|||||||
Routes,
|
Routes,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { HintComponent } from './accounts/hint.component';
|
||||||
|
import { LoginComponent } from './accounts/login.component';
|
||||||
|
import { RegisterComponent } from './accounts/register.component';
|
||||||
|
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||||
|
|
||||||
import { VaultComponent } from './vault/vault.component';
|
import { VaultComponent } from './vault/vault.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: '/vault', pathMatch: 'full' },
|
{ path: '', redirectTo: '/login', pathMatch: 'full' },
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: '2fa', component: TwoFactorComponent },
|
||||||
|
{ path: 'register', component: RegisterComponent },
|
||||||
|
{ path: 'hint', component: HintComponent },
|
||||||
{
|
{
|
||||||
path: 'vault',
|
path: 'vault',
|
||||||
component: VaultComponent,
|
component: VaultComponent,
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import 'core-js';
|
import 'core-js';
|
||||||
import 'zone.js/dist/zone';
|
import 'zone.js/dist/zone';
|
||||||
|
|
||||||
|
import { ToasterModule } from 'angular2-toaster';
|
||||||
|
import { Angulartics2Module } from 'angulartics2';
|
||||||
|
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
@ -8,19 +12,59 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { ServicesModule } from './services.module';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
import { HintComponent } from './accounts/hint.component';
|
||||||
|
import { LoginComponent } from './accounts/login.component';
|
||||||
|
import { RegisterComponent } from './accounts/register.component';
|
||||||
|
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||||
|
|
||||||
import { VaultComponent } from './vault/vault.component';
|
import { VaultComponent } from './vault/vault.component';
|
||||||
|
|
||||||
|
import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive';
|
||||||
|
import { AutofocusDirective } from 'jslib/angular/directives/autofocus.directive';
|
||||||
|
import { BlurClickDirective } from 'jslib/angular/directives/blur-click.directive';
|
||||||
|
import { BoxRowDirective } from 'jslib/angular/directives/box-row.directive';
|
||||||
|
import { FallbackSrcDirective } from 'jslib/angular/directives/fallback-src.directive';
|
||||||
|
import { InputVerbatimDirective } from 'jslib/angular/directives/input-verbatim.directive';
|
||||||
|
import { StopClickDirective } from 'jslib/angular/directives/stop-click.directive';
|
||||||
|
import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive';
|
||||||
|
|
||||||
|
import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
||||||
|
import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
|
ServicesModule,
|
||||||
|
Angulartics2Module.forRoot([Angulartics2GoogleAnalytics], {
|
||||||
|
pageTracking: {
|
||||||
|
clearQueryParams: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ToasterModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
ApiActionDirective,
|
||||||
AppComponent,
|
AppComponent,
|
||||||
|
AutofocusDirective,
|
||||||
|
BlurClickDirective,
|
||||||
|
BoxRowDirective,
|
||||||
|
FallbackSrcDirective,
|
||||||
|
HintComponent,
|
||||||
|
I18nPipe,
|
||||||
|
InputVerbatimDirective,
|
||||||
|
LoginComponent,
|
||||||
|
RegisterComponent,
|
||||||
|
SearchCiphersPipe,
|
||||||
|
StopClickDirective,
|
||||||
|
StopPropDirective,
|
||||||
|
TwoFactorComponent,
|
||||||
VaultComponent,
|
VaultComponent,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
|
167
src/app/services.module.ts
Normal file
167
src/app/services.module.ts
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import {
|
||||||
|
APP_INITIALIZER,
|
||||||
|
NgModule,
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { ToasterModule } from 'angular2-toaster';
|
||||||
|
|
||||||
|
import { I18nService } from '../services/i18n.service';
|
||||||
|
import { WebMessagingService } from '../services/webMessaging.service';
|
||||||
|
import { WebPlatformUtilsService } from '../services/webPlatformUtils.service';
|
||||||
|
import { WebStorageService } from '../services/webStorage.service';
|
||||||
|
|
||||||
|
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
|
||||||
|
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
||||||
|
import { ValidationService } from 'jslib/angular/services/validation.service';
|
||||||
|
|
||||||
|
import { Analytics } from 'jslib/misc/analytics';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib/services/api.service';
|
||||||
|
import { AppIdService } from 'jslib/services/appId.service';
|
||||||
|
import { AuditService } from 'jslib/services/audit.service';
|
||||||
|
import { AuthService } from 'jslib/services/auth.service';
|
||||||
|
import { CipherService } from 'jslib/services/cipher.service';
|
||||||
|
import { CollectionService } from 'jslib/services/collection.service';
|
||||||
|
import { ConstantsService } from 'jslib/services/constants.service';
|
||||||
|
import { ContainerService } from 'jslib/services/container.service';
|
||||||
|
import { CryptoService } from 'jslib/services/crypto.service';
|
||||||
|
import { EnvironmentService } from 'jslib/services/environment.service';
|
||||||
|
import { ExportService } from 'jslib/services/export.service';
|
||||||
|
import { FolderService } from 'jslib/services/folder.service';
|
||||||
|
import { LockService } from 'jslib/services/lock.service';
|
||||||
|
import { PasswordGenerationService } from 'jslib/services/passwordGeneration.service';
|
||||||
|
import { SettingsService } from 'jslib/services/settings.service';
|
||||||
|
import { StateService } from 'jslib/services/state.service';
|
||||||
|
import { SyncService } from 'jslib/services/sync.service';
|
||||||
|
import { TokenService } from 'jslib/services/token.service';
|
||||||
|
import { TotpService } from 'jslib/services/totp.service';
|
||||||
|
import { UserService } from 'jslib/services/user.service';
|
||||||
|
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
|
||||||
|
|
||||||
|
import { ApiService as ApiServiceAbstraction } from 'jslib/abstractions/api.service';
|
||||||
|
import { AppIdService as AppIdServiceAbstraction } from 'jslib/abstractions/appId.service';
|
||||||
|
import { AuditService as AuditServiceAbstraction } from 'jslib/abstractions/audit.service';
|
||||||
|
import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.service';
|
||||||
|
import { CipherService as CipherServiceAbstraction } from 'jslib/abstractions/cipher.service';
|
||||||
|
import { CollectionService as CollectionServiceAbstraction } from 'jslib/abstractions/collection.service';
|
||||||
|
import { CryptoService as CryptoServiceAbstraction } from 'jslib/abstractions/crypto.service';
|
||||||
|
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib/abstractions/cryptoFunction.service';
|
||||||
|
import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib/abstractions/environment.service';
|
||||||
|
import { ExportService as ExportServiceAbstraction } from 'jslib/abstractions/export.service';
|
||||||
|
import { FolderService as FolderServiceAbstraction } from 'jslib/abstractions/folder.service';
|
||||||
|
import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { LockService as LockServiceAbstraction } from 'jslib/abstractions/lock.service';
|
||||||
|
import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service';
|
||||||
|
import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service';
|
||||||
|
import {
|
||||||
|
PasswordGenerationService as PasswordGenerationServiceAbstraction,
|
||||||
|
} from 'jslib/abstractions/passwordGeneration.service';
|
||||||
|
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
import { SettingsService as SettingsServiceAbstraction } from 'jslib/abstractions/settings.service';
|
||||||
|
import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service';
|
||||||
|
import { StorageService as StorageServiceAbstraction } from 'jslib/abstractions/storage.service';
|
||||||
|
import { SyncService as SyncServiceAbstraction } from 'jslib/abstractions/sync.service';
|
||||||
|
import { TokenService as TokenServiceAbstraction } from 'jslib/abstractions/token.service';
|
||||||
|
import { TotpService as TotpServiceAbstraction } from 'jslib/abstractions/totp.service';
|
||||||
|
import { UserService as UserServiceAbstraction } from 'jslib/abstractions/user.service';
|
||||||
|
|
||||||
|
const i18nService = new I18nService(window.navigator.language, 'locales');
|
||||||
|
const stateService = new StateService();
|
||||||
|
const broadcasterService = new BroadcasterService();
|
||||||
|
const messagingService = new WebMessagingService();
|
||||||
|
const platformUtilsService = new WebPlatformUtilsService(messagingService);
|
||||||
|
const storageService: StorageServiceAbstraction = new WebStorageService();
|
||||||
|
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new WebCryptoFunctionService(window,
|
||||||
|
platformUtilsService);
|
||||||
|
const cryptoService = new CryptoService(storageService, storageService, cryptoFunctionService);
|
||||||
|
const tokenService = new TokenService(storageService);
|
||||||
|
const appIdService = new AppIdService(storageService);
|
||||||
|
const apiService = new ApiService(tokenService, platformUtilsService,
|
||||||
|
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
||||||
|
const environmentService = new EnvironmentService(apiService, storageService);
|
||||||
|
const userService = new UserService(tokenService, storageService);
|
||||||
|
const settingsService = new SettingsService(userService, storageService);
|
||||||
|
const cipherService = new CipherService(cryptoService, userService, settingsService,
|
||||||
|
apiService, storageService, i18nService, platformUtilsService);
|
||||||
|
const folderService = new FolderService(cryptoService, userService,
|
||||||
|
() => i18nService.t('noneFolder'), apiService, storageService, i18nService);
|
||||||
|
const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService);
|
||||||
|
const lockService = new LockService(cipherService, folderService, collectionService,
|
||||||
|
cryptoService, platformUtilsService, storageService, messagingService, null);
|
||||||
|
const syncService = new SyncService(userService, apiService, settingsService,
|
||||||
|
folderService, cipherService, cryptoService, collectionService, storageService, messagingService,
|
||||||
|
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
||||||
|
const passwordGenerationService = new PasswordGenerationService(cryptoService, storageService);
|
||||||
|
const totpService = new TotpService(storageService, cryptoFunctionService);
|
||||||
|
const containerService = new ContainerService(cryptoService, platformUtilsService);
|
||||||
|
const authService = new AuthService(cryptoService, apiService,
|
||||||
|
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService);
|
||||||
|
const exportService = new ExportService(folderService, cipherService);
|
||||||
|
const auditService = new AuditService(cryptoFunctionService);
|
||||||
|
|
||||||
|
const analytics = new Analytics(window, () => platformUtilsService.isDev(),
|
||||||
|
platformUtilsService, storageService, appIdService);
|
||||||
|
containerService.attachToWindow(window);
|
||||||
|
environmentService.setUrlsFromStorage().then(() => {
|
||||||
|
return syncService.fullSync(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
export function initFactory(): Function {
|
||||||
|
return async () => {
|
||||||
|
lockService.init(true);
|
||||||
|
const locale = await storageService.get<string>(ConstantsService.localeKey);
|
||||||
|
await i18nService.init(locale);
|
||||||
|
await authService.init();
|
||||||
|
const htmlEl = window.document.documentElement;
|
||||||
|
htmlEl.classList.add('os_' + platformUtilsService.getDeviceString());
|
||||||
|
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
||||||
|
let theme = await storageService.get<string>(ConstantsService.themeKey);
|
||||||
|
if (theme == null) {
|
||||||
|
theme = 'light';
|
||||||
|
}
|
||||||
|
htmlEl.classList.add('theme_' + theme);
|
||||||
|
stateService.save(ConstantsService.disableFaviconKey,
|
||||||
|
await storageService.get<boolean>(ConstantsService.disableFaviconKey));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
ToasterModule,
|
||||||
|
],
|
||||||
|
declarations: [],
|
||||||
|
providers: [
|
||||||
|
ValidationService,
|
||||||
|
AuthGuardService,
|
||||||
|
{ provide: AuditServiceAbstraction, useValue: auditService },
|
||||||
|
{ provide: AuthServiceAbstraction, useValue: authService },
|
||||||
|
{ provide: CipherServiceAbstraction, useValue: cipherService },
|
||||||
|
{ provide: FolderServiceAbstraction, useValue: folderService },
|
||||||
|
{ provide: CollectionServiceAbstraction, useValue: collectionService },
|
||||||
|
{ provide: EnvironmentServiceAbstraction, useValue: environmentService },
|
||||||
|
{ provide: TotpServiceAbstraction, useValue: totpService },
|
||||||
|
{ provide: TokenServiceAbstraction, useValue: tokenService },
|
||||||
|
{ provide: I18nServiceAbstraction, useValue: i18nService },
|
||||||
|
{ provide: CryptoServiceAbstraction, useValue: cryptoService },
|
||||||
|
{ provide: PlatformUtilsServiceAbstraction, useValue: platformUtilsService },
|
||||||
|
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
|
||||||
|
{ provide: ApiServiceAbstraction, useValue: apiService },
|
||||||
|
{ provide: SyncServiceAbstraction, useValue: syncService },
|
||||||
|
{ provide: UserServiceAbstraction, useValue: userService },
|
||||||
|
{ provide: MessagingServiceAbstraction, useValue: messagingService },
|
||||||
|
{ provide: BroadcasterService, useValue: broadcasterService },
|
||||||
|
{ provide: SettingsServiceAbstraction, useValue: settingsService },
|
||||||
|
{ provide: LockServiceAbstraction, useValue: lockService },
|
||||||
|
{ provide: StorageServiceAbstraction, useValue: storageService },
|
||||||
|
{ provide: StateServiceAbstraction, useValue: stateService },
|
||||||
|
{ provide: ExportServiceAbstraction, useValue: exportService },
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: initFactory,
|
||||||
|
deps: [],
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ServicesModule {
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
<b>The vault!!</b>
|
<b>The vault!! {{ 'hello' | i18n }}</b>
|
||||||
|
5
src/locales/en/messages.json
Normal file
5
src/locales/en/messages.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"hello": {
|
||||||
|
"message": "hello world"
|
||||||
|
}
|
||||||
|
}
|
5
src/locales/es/messages.json
Normal file
5
src/locales/es/messages.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"hello": {
|
||||||
|
"message": "hola mundo"
|
||||||
|
}
|
||||||
|
}
|
16
src/services/i18n.service.ts
Normal file
16
src/services/i18n.service.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { I18nService as BaseI18nService } from 'jslib/services/i18n.service';
|
||||||
|
|
||||||
|
export class I18nService extends BaseI18nService {
|
||||||
|
constructor(systemLanguage: string, localesDirectory: string) {
|
||||||
|
super(systemLanguage, localesDirectory, async (formattedLocale: string) => {
|
||||||
|
const filePath = this.localesDirectory + '/' + formattedLocale + '/messages.json';
|
||||||
|
const localesResult = await fetch(filePath);
|
||||||
|
const locales = await localesResult.json();
|
||||||
|
return locales;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.supportedTranslationLocales = [
|
||||||
|
'en', 'es',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
7
src/services/webMessaging.service.ts
Normal file
7
src/services/webMessaging.service.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||||
|
|
||||||
|
export class WebMessagingService implements MessagingService {
|
||||||
|
send(subscriber: string, arg: any = {}) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
181
src/services/webPlatformUtils.service.ts
Normal file
181
src/services/webPlatformUtils.service.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { DeviceType } from 'jslib/enums/deviceType';
|
||||||
|
|
||||||
|
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
|
||||||
|
import { AnalyticsIds } from 'jslib/misc/analytics';
|
||||||
|
|
||||||
|
const DialogPromiseExpiration = 600000; // 10 minutes
|
||||||
|
|
||||||
|
export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||||
|
identityClientId: string = 'web';
|
||||||
|
|
||||||
|
private showDialogResolves = new Map<number, { resolve: (value: boolean) => void, date: Date }>();
|
||||||
|
private deviceCache: DeviceType = null;
|
||||||
|
private analyticsIdCache: string = null;
|
||||||
|
|
||||||
|
constructor(private messagingService: MessagingService) { }
|
||||||
|
|
||||||
|
getDevice(): DeviceType {
|
||||||
|
if (this.deviceCache) {
|
||||||
|
return this.deviceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.userAgent.indexOf(' Firefox/') !== -1 || navigator.userAgent.indexOf(' Gecko/') !== -1) {
|
||||||
|
this.deviceCache = DeviceType.Firefox;
|
||||||
|
} else if (navigator.userAgent.indexOf(' OPR/') >= 0) {
|
||||||
|
this.deviceCache = DeviceType.Opera;
|
||||||
|
} else if (navigator.userAgent.indexOf(' Edge/') !== -1) {
|
||||||
|
this.deviceCache = DeviceType.Edge;
|
||||||
|
} else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) {
|
||||||
|
this.deviceCache = DeviceType.Vivaldi;
|
||||||
|
} else if (navigator.userAgent.indexOf(' Safari/') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
|
||||||
|
this.deviceCache = DeviceType.Safari;
|
||||||
|
} else if ((window as any).chrome && navigator.userAgent.indexOf(' Chrome/') !== -1) {
|
||||||
|
this.deviceCache = DeviceType.Chrome;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.deviceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceString(): string {
|
||||||
|
return DeviceType[this.getDevice()].toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirefox(): boolean {
|
||||||
|
return this.getDevice() === DeviceType.Firefox;
|
||||||
|
}
|
||||||
|
|
||||||
|
isChrome(): boolean {
|
||||||
|
return this.getDevice() === DeviceType.Chrome;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEdge(): boolean {
|
||||||
|
return this.getDevice() === DeviceType.Edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
isOpera(): boolean {
|
||||||
|
return this.getDevice() === DeviceType.Opera;
|
||||||
|
}
|
||||||
|
|
||||||
|
isVivaldi(): boolean {
|
||||||
|
return this.getDevice() === DeviceType.Vivaldi;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSafari(): boolean {
|
||||||
|
return this.getDevice() === DeviceType.Safari;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMacAppStore(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
analyticsId(): string {
|
||||||
|
if (this.analyticsIdCache) {
|
||||||
|
return this.analyticsIdCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.analyticsIdCache = (AnalyticsIds as any)[this.getDevice()];
|
||||||
|
return this.analyticsIdCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDomain(uriString: string): string {
|
||||||
|
return uriString;
|
||||||
|
}
|
||||||
|
|
||||||
|
isViewOpen(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
launchUri(uri: string, options?: any): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
getApplicationVersion(): string {
|
||||||
|
return '1.2.3';
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsU2f(win: Window): boolean {
|
||||||
|
if (win != null && (win as any).u2f != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isChrome() || this.isOpera();
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsDuo(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast(type: 'error' | 'success' | 'warning' | 'info', title: string, text: string, global?: any): void {
|
||||||
|
throw new Error('showToast not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog(text: string, title?: string, confirmText?: string, cancelText?: string, type?: string) {
|
||||||
|
const dialogId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||||
|
this.messagingService.send('showDialog', {
|
||||||
|
text: text,
|
||||||
|
title: title,
|
||||||
|
confirmText: confirmText,
|
||||||
|
cancelText: cancelText,
|
||||||
|
type: type,
|
||||||
|
dialogId: dialogId,
|
||||||
|
});
|
||||||
|
return new Promise<boolean>((resolve) => {
|
||||||
|
this.showDialogResolves.set(dialogId, { resolve: resolve, date: new Date() });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isDev(): boolean {
|
||||||
|
return process.env.ENV === 'development';
|
||||||
|
}
|
||||||
|
|
||||||
|
copyToClipboard(text: string, options?: any): void {
|
||||||
|
const doc = options ? options.doc : window.document;
|
||||||
|
if ((window as any).clipboardData && (window as any).clipboardData.setData) {
|
||||||
|
// IE specific code path to prevent textarea being shown while dialog is visible.
|
||||||
|
(window as any).clipboardData.setData('Text', text);
|
||||||
|
} else if (doc.queryCommandSupported && doc.queryCommandSupported('copy')) {
|
||||||
|
const textarea = doc.createElement('textarea');
|
||||||
|
textarea.textContent = text;
|
||||||
|
// Prevent scrolling to bottom of page in MS Edge.
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
doc.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Security exception may be thrown by some browsers.
|
||||||
|
doc.execCommand('copy');
|
||||||
|
} catch (e) {
|
||||||
|
// tslint:disable-next-line
|
||||||
|
console.warn('Copy to clipboard failed.', e);
|
||||||
|
} finally {
|
||||||
|
doc.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveDialogPromise(dialogId: number, confirmed: boolean) {
|
||||||
|
if (this.showDialogResolves.has(dialogId)) {
|
||||||
|
const resolveObj = this.showDialogResolves.get(dialogId);
|
||||||
|
resolveObj.resolve(confirmed);
|
||||||
|
this.showDialogResolves.delete(dialogId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up old promises
|
||||||
|
const deleteIds: number[] = [];
|
||||||
|
this.showDialogResolves.forEach((val, key) => {
|
||||||
|
const age = new Date().getTime() - val.date.getTime();
|
||||||
|
if (age > DialogPromiseExpiration) {
|
||||||
|
deleteIds.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deleteIds.forEach((id) => {
|
||||||
|
this.showDialogResolves.delete(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
24
src/services/webStorage.service.ts
Normal file
24
src/services/webStorage.service.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||||
|
|
||||||
|
export class WebStorageService implements StorageService {
|
||||||
|
private store: any = {};
|
||||||
|
|
||||||
|
get<T>(key: string): Promise<T> {
|
||||||
|
const val = this.store[key];
|
||||||
|
if (val == null) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
return Promise.resolve(val as T);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(key: string, obj: any): Promise<any> {
|
||||||
|
this.store[key] = obj;
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key: string): Promise<any> {
|
||||||
|
delete this.store[key];
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,5 +29,6 @@
|
|||||||
"jslib/src/angular/dummy.module.ts",
|
"jslib/src/angular/dummy.module.ts",
|
||||||
"jslib/src/services/nodeApi.service.ts",
|
"jslib/src/services/nodeApi.service.ts",
|
||||||
"jslib/src/services/lowdbStorage.service.ts",
|
"jslib/src/services/lowdbStorage.service.ts",
|
||||||
|
"jslib/src/misc/nodeUtils.ts",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ const plugins = [
|
|||||||
{ from: './src/browserconfig.xml' },
|
{ from: './src/browserconfig.xml' },
|
||||||
{ from: './src/app-id.json' },
|
{ from: './src/app-id.json' },
|
||||||
{ from: './src/images', to: 'images' },
|
{ from: './src/images', to: 'images' },
|
||||||
|
{ from: './src/locales', to: 'locales' },
|
||||||
]),
|
]),
|
||||||
extractCss,
|
extractCss,
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
|
Loading…
Reference in New Issue
Block a user