mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 11:43:46 +00:00
Apply Prettier (#2238)
This commit is contained in:
@@ -1,67 +1,104 @@
|
||||
<form #form (ngSubmit)="submit()">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{'close' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{ "close" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "appName" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{ "save" | i18n }}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{ "selfHostedEnvironment" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="baseUrl"
|
||||
type="text"
|
||||
name="BaseUrl"
|
||||
[(ngModel)]="baseUrl"
|
||||
placeholder="ex. https://bitwarden.company.com"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'appName' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{'save' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "selfHostedEnvironmentFooter" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{ "customEnvironment" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content" [hidden]="!showCustom">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="webVaultUrl">{{ "webVaultUrl" | i18n }}</label>
|
||||
<input
|
||||
id="webVaultUrl"
|
||||
type="text"
|
||||
name="WebVaultUrl"
|
||||
[(ngModel)]="webVaultUrl"
|
||||
inputmode="url"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{'selfHostedEnvironment' | i18n}}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="baseUrl">{{'baseUrl' | i18n}}</label>
|
||||
<input id="baseUrl" type="text" name="BaseUrl" [(ngModel)]="baseUrl"
|
||||
placeholder="ex. https://bitwarden.company.com" appInputVerbatim>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'selfHostedEnvironmentFooter' | i18n}}
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="apiUrl">{{ "apiUrl" | i18n }}</label>
|
||||
<input
|
||||
id="apiUrl"
|
||||
type="text"
|
||||
name="ApiUrl"
|
||||
[(ngModel)]="apiUrl"
|
||||
inputmode="url"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{'customEnvironment' | i18n}}
|
||||
</h2>
|
||||
<div class="box-content" [hidden]="!showCustom">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="webVaultUrl">{{'webVaultUrl' | i18n}}</label>
|
||||
<input id="webVaultUrl" type="text" name="WebVaultUrl" [(ngModel)]="webVaultUrl" inputmode="url"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="apiUrl">{{'apiUrl' | i18n}}</label>
|
||||
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl" inputmode="url" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="identityUrl">{{'identityUrl' | i18n}}</label>
|
||||
<input id="identityUrl" type="text" name="IdentityUrl" [(ngModel)]="identityUrl" inputmode="url"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notificationsUrl">{{'notificationsUrl' | i18n}}</label>
|
||||
<input id="notificationsUrl" type="text" name="NotificationsUrl" inputmode="url"
|
||||
[(ngModel)]="notificationsUrl" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="iconsUrl">{{'iconsUrl' | i18n}}</label>
|
||||
<input id="iconsUrl" type="text" name="IconsUrl" [(ngModel)]="iconsUrl" inputmode="url"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" [hidden]="!showCustom">
|
||||
{{'customEnvironmentFooter' | i18n}}
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="identityUrl">{{ "identityUrl" | i18n }}</label>
|
||||
<input
|
||||
id="identityUrl"
|
||||
type="text"
|
||||
name="IdentityUrl"
|
||||
[(ngModel)]="identityUrl"
|
||||
inputmode="url"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</content>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notificationsUrl">{{ "notificationsUrl" | i18n }}</label>
|
||||
<input
|
||||
id="notificationsUrl"
|
||||
type="text"
|
||||
name="NotificationsUrl"
|
||||
inputmode="url"
|
||||
[(ngModel)]="notificationsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="iconsUrl">{{ "iconsUrl" | i18n }}</label>
|
||||
<input
|
||||
id="iconsUrl"
|
||||
type="text"
|
||||
name="IconsUrl"
|
||||
[(ngModel)]="iconsUrl"
|
||||
inputmode="url"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" [hidden]="!showCustom">
|
||||
{{ "customEnvironmentFooter" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib-angular/components/environment.component';
|
||||
import { EnvironmentComponent as BaseEnvironmentComponent } from "jslib-angular/components/environment.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-environment',
|
||||
templateUrl: 'environment.component.html',
|
||||
selector: "app-environment",
|
||||
templateUrl: "environment.component.html",
|
||||
})
|
||||
export class EnvironmentComponent extends BaseEnvironmentComponent {
|
||||
constructor(platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
|
||||
i18nService: I18nService, private router: Router) {
|
||||
super(platformUtilsService, environmentService, i18nService);
|
||||
this.showCustom = true;
|
||||
}
|
||||
constructor(
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
i18nService: I18nService,
|
||||
private router: Router
|
||||
) {
|
||||
super(platformUtilsService, environmentService, i18nService);
|
||||
this.showCustom = true;
|
||||
}
|
||||
|
||||
saved() {
|
||||
super.saved();
|
||||
this.router.navigate(['']);
|
||||
}
|
||||
saved() {
|
||||
super.saved();
|
||||
this.router.navigate([""]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/login">{{'cancel' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/login">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "passwordHint" | i18n }}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></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
|
||||
inputmode="email"
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'passwordHint' | i18n}}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></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
|
||||
inputmode="email" appInputVerbatim="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'enterEmailToGetHint' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "enterEmailToGetHint" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { HintComponent as BaseHintComponent } from 'jslib-angular/components/hint.component';
|
||||
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-hint',
|
||||
templateUrl: 'hint.component.html',
|
||||
selector: "app-hint",
|
||||
templateUrl: "hint.component.html",
|
||||
})
|
||||
export class HintComponent extends BaseHintComponent {
|
||||
constructor(router: Router, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService, apiService: ApiService, logService: LogService) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||
}
|
||||
constructor(
|
||||
router: Router,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
apiService: ApiService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<div class="center-content">
|
||||
<div class="content">
|
||||
<div class="logo-image"></div>
|
||||
<p class="lead text-center">{{'loginOrCreateNewAccount' | i18n}}</p>
|
||||
<a class="btn primary block" routerLink="/login"><b>{{'login' | i18n}}</b></a>
|
||||
<button type="button" (click)="launchSsoBrowser()" class="btn block">
|
||||
<i class="fa fa-bank" aria-hidden="true"></i> {{'enterpriseSingleSignOn' | i18n}}
|
||||
</button>
|
||||
<a class="btn block" routerLink="/register">{{'createAccount' | i18n}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="logo-image"></div>
|
||||
<p class="lead text-center">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||
<a class="btn primary block" routerLink="/login"
|
||||
><b>{{ "login" | i18n }}</b></a
|
||||
>
|
||||
<button type="button" (click)="launchSsoBrowser()" class="btn block">
|
||||
<i class="fa fa-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||
</button>
|
||||
<a class="btn block" routerLink="/register">{{ "createAccount" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<a routerLink="/environment" class="settings-icon">
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i><span> {{'settings' | i18n}}</span>
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i><span> {{ "settings" | i18n }}</span>
|
||||
</a>
|
||||
|
||||
@@ -1,53 +1,66 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: 'home.component.html',
|
||||
selector: "app-home",
|
||||
templateUrl: "home.component.html",
|
||||
})
|
||||
export class HomeComponent {
|
||||
constructor(protected platformUtilsService: PlatformUtilsService,
|
||||
private passwordGenerationService: PasswordGenerationService, private storageService: StorageService,
|
||||
private cryptoFunctionService: CryptoFunctionService, private environmentService: EnvironmentService) { }
|
||||
constructor(
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private passwordGenerationService: PasswordGenerationService,
|
||||
private storageService: StorageService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private environmentService: EnvironmentService
|
||||
) {}
|
||||
|
||||
async launchSsoBrowser() {
|
||||
// Generate necessary sso params
|
||||
const passwordOptions: any = {
|
||||
type: 'password',
|
||||
length: 64,
|
||||
uppercase: true,
|
||||
lowercase: true,
|
||||
numbers: true,
|
||||
special: false,
|
||||
};
|
||||
async launchSsoBrowser() {
|
||||
// Generate necessary sso params
|
||||
const passwordOptions: any = {
|
||||
type: "password",
|
||||
length: 64,
|
||||
uppercase: true,
|
||||
lowercase: true,
|
||||
numbers: true,
|
||||
special: false,
|
||||
};
|
||||
|
||||
const state = (await this.passwordGenerationService.generatePassword(passwordOptions)) + ':clientId=browser';
|
||||
const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
|
||||
const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, 'sha256');
|
||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
const state =
|
||||
(await this.passwordGenerationService.generatePassword(passwordOptions)) +
|
||||
":clientId=browser";
|
||||
const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
|
||||
const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256");
|
||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
|
||||
await this.storageService.save(ConstantsService.ssoCodeVerifierKey, codeVerifier);
|
||||
await this.storageService.save(ConstantsService.ssoStateKey, state);
|
||||
await this.storageService.save(ConstantsService.ssoCodeVerifierKey, codeVerifier);
|
||||
await this.storageService.save(ConstantsService.ssoStateKey, state);
|
||||
|
||||
let url = this.environmentService.getWebVaultUrl();
|
||||
if (url == null) {
|
||||
url = 'https://vault.bitwarden.com';
|
||||
}
|
||||
|
||||
const redirectUri = url + '/sso-connector.html';
|
||||
|
||||
// Launch browser
|
||||
this.platformUtilsService.launchUri(url + '/#/sso?clientId=browser' +
|
||||
'&redirectUri=' + encodeURIComponent(redirectUri) +
|
||||
'&state=' + state + '&codeChallenge=' + codeChallenge);
|
||||
let url = this.environmentService.getWebVaultUrl();
|
||||
if (url == null) {
|
||||
url = "https://vault.bitwarden.com";
|
||||
}
|
||||
|
||||
const redirectUri = url + "/sso-connector.html";
|
||||
|
||||
// Launch browser
|
||||
this.platformUtilsService.launchUri(
|
||||
url +
|
||||
"/#/sso?clientId=browser" +
|
||||
"&redirectUri=" +
|
||||
encodeURIComponent(redirectUri) +
|
||||
"&state=" +
|
||||
state +
|
||||
"&codeChallenge=" +
|
||||
codeChallenge
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,74 @@
|
||||
<form (ngSubmit)="submit()">
|
||||
<header>
|
||||
<div class="left"></div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'verifyIdentity' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick *ngIf="!hideInput">{{'unlock' | i18n}}</button>
|
||||
<header>
|
||||
<div class="left"></div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "verifyIdentity" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick *ngIf="!hideInput">{{ "unlock" | i18n }}</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||
<div class="row-main" *ngIf="pinLock">
|
||||
<label for="pin">{{ "pin" | i18n }}</label>
|
||||
<input
|
||||
id="pin"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="PIN"
|
||||
class="monospaced"
|
||||
[(ngModel)]="pin"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="row-main" *ngIf="!pinLock">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||
<div class="row-main" *ngIf="pinLock">
|
||||
<label for="pin">{{'pin' | i18n}}</label>
|
||||
<input id="pin" type="{{showPassword ? 'text' : 'password'}}" name="PIN" class="monospaced"
|
||||
[(ngModel)]="pin" required appInputVerbatim>
|
||||
</div>
|
||||
<div class="row-main" *ngIf="!pinLock">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{'yourVaultIsLocked' | i18n}}</p>
|
||||
{{'loggedInAsOn' | i18n : email : webVaultHostname}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="biometricLock">
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn primary block" (click)="unlockBiometric()"
|
||||
appStopClick>{{'unlockWithBiometrics' | i18n}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center">
|
||||
<button type="button" appStopClick (click)="logOut()">{{'logOut' | i18n}}</button>
|
||||
</p>
|
||||
</content>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{ "yourVaultIsLocked" | i18n }}</p>
|
||||
{{ "loggedInAsOn" | i18n: email:webVaultHostname }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="biometricLock">
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn primary block" (click)="unlockBiometric()" appStopClick>
|
||||
{{ "unlockWithBiometrics" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center">
|
||||
<button type="button" appStopClick (click)="logOut()">{{ "logOut" | i18n }}</button>
|
||||
</p>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,87 +1,107 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import Swal from 'sweetalert2';
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { LockComponent as BaseLockComponent } from 'jslib-angular/components/lock.component';
|
||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-lock',
|
||||
templateUrl: 'lock.component.html',
|
||||
selector: "app-lock",
|
||||
templateUrl: "lock.component.html",
|
||||
})
|
||||
export class LockComponent extends BaseLockComponent {
|
||||
private isInitialLockScreen: boolean;
|
||||
private isInitialLockScreen: boolean;
|
||||
|
||||
constructor(router: Router, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
|
||||
userService: UserService, cryptoService: CryptoService,
|
||||
storageService: StorageService, vaultTimeoutService: VaultTimeoutService,
|
||||
environmentService: EnvironmentService, stateService: StateService,
|
||||
apiService: ApiService, logService: LogService, keyConnectorService: KeyConnectorService,
|
||||
ngZone: NgZone) {
|
||||
super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService,
|
||||
storageService, vaultTimeoutService, environmentService, stateService, apiService, logService,
|
||||
keyConnectorService, ngZone);
|
||||
this.successRoute = '/tabs/current';
|
||||
this.isInitialLockScreen = (window as any).previousPopupUrl == null;
|
||||
}
|
||||
constructor(
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
messagingService: MessagingService,
|
||||
userService: UserService,
|
||||
cryptoService: CryptoService,
|
||||
storageService: StorageService,
|
||||
vaultTimeoutService: VaultTimeoutService,
|
||||
environmentService: EnvironmentService,
|
||||
stateService: StateService,
|
||||
apiService: ApiService,
|
||||
logService: LogService,
|
||||
keyConnectorService: KeyConnectorService,
|
||||
ngZone: NgZone
|
||||
) {
|
||||
super(
|
||||
router,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
messagingService,
|
||||
userService,
|
||||
cryptoService,
|
||||
storageService,
|
||||
vaultTimeoutService,
|
||||
environmentService,
|
||||
stateService,
|
||||
apiService,
|
||||
logService,
|
||||
keyConnectorService,
|
||||
ngZone
|
||||
);
|
||||
this.successRoute = "/tabs/current";
|
||||
this.isInitialLockScreen = (window as any).previousPopupUrl == null;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const disableAutoBiometricsPrompt = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableAutoBiometricsPromptKey) ?? true;
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const disableAutoBiometricsPrompt =
|
||||
(await this.storageService.get<boolean>(ConstantsService.disableAutoBiometricsPromptKey)) ??
|
||||
true;
|
||||
|
||||
window.setTimeout(async () => {
|
||||
document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus();
|
||||
if (this.biometricLock && !disableAutoBiometricsPrompt && this.isInitialLockScreen) {
|
||||
if (await this.vaultTimeoutService.isLocked()) {
|
||||
await this.unlockBiometric();
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async unlockBiometric(): Promise<boolean> {
|
||||
if (!this.biometricLock) {
|
||||
return;
|
||||
window.setTimeout(async () => {
|
||||
document.getElementById(this.pinLock ? "pin" : "masterPassword").focus();
|
||||
if (this.biometricLock && !disableAutoBiometricsPrompt && this.isInitialLockScreen) {
|
||||
if (await this.vaultTimeoutService.isLocked()) {
|
||||
await this.unlockBiometric();
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `<div class="swal2-text">${this.i18nService.t('awaitDesktop')}</div>`;
|
||||
|
||||
Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t('cancel'),
|
||||
showConfirmButton: false,
|
||||
});
|
||||
|
||||
const success = await super.unlockBiometric();
|
||||
|
||||
// Avoid closing the error dialogs
|
||||
if (success) {
|
||||
Swal.close();
|
||||
}
|
||||
|
||||
return success;
|
||||
async unlockBiometric(): Promise<boolean> {
|
||||
if (!this.biometricLock) {
|
||||
return;
|
||||
}
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = `<div class="swal2-text">${this.i18nService.t("awaitDesktop")}</div>`;
|
||||
|
||||
Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t("cancel"),
|
||||
showConfirmButton: false,
|
||||
});
|
||||
|
||||
const success = await super.unlockBiometric();
|
||||
|
||||
// Avoid closing the error dialogs
|
||||
if (success) {
|
||||
Swal.close();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,71 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{'cancel' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "appName" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{ "login" | i18n }}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></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
|
||||
inputmode="email"
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'appName' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{'login' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
<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
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</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 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 appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
<p class="text-center">
|
||||
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
|
||||
</p>
|
||||
</content>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center">
|
||||
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||
</p>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,42 +1,58 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from 'jslib-angular/components/login.component';
|
||||
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: 'login.component.html',
|
||||
selector: "app-login",
|
||||
templateUrl: "login.component.html",
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
protected platformUtilsService: PlatformUtilsService, protected i18nService: I18nService,
|
||||
protected stateService: StateService, protected environmentService: EnvironmentService,
|
||||
protected passwordGenerationService: PasswordGenerationService,
|
||||
protected cryptoFunctionService: CryptoFunctionService, storageService: StorageService,
|
||||
syncService: SyncService, logService: LogService, ngZone: NgZone) {
|
||||
super(authService, router, platformUtilsService, i18nService, stateService, environmentService,
|
||||
passwordGenerationService, cryptoFunctionService, storageService, logService, ngZone);
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
};
|
||||
super.successRoute = '/tabs/vault';
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected i18nService: I18nService,
|
||||
protected stateService: StateService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected passwordGenerationService: PasswordGenerationService,
|
||||
protected cryptoFunctionService: CryptoFunctionService,
|
||||
storageService: StorageService,
|
||||
syncService: SyncService,
|
||||
logService: LogService,
|
||||
ngZone: NgZone
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
platformUtilsService,
|
||||
i18nService,
|
||||
stateService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
cryptoFunctionService,
|
||||
storageService,
|
||||
logService,
|
||||
ngZone
|
||||
);
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
};
|
||||
super.successRoute = "/tabs/vault";
|
||||
}
|
||||
|
||||
settings() {
|
||||
this.router.navigate(['environment']);
|
||||
}
|
||||
settings() {
|
||||
this.router.navigate(["environment"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,160 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{'cancel' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "createAccount" | i18n }}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></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>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'createAccount' | i18n}}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></i>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||
*ngIf="masterPasswordScoreText"
|
||||
>
|
||||
{{ masterPasswordScoreText }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
[appAutofocus]="email !== ''"
|
||||
appInputVerbatim
|
||||
(input)="updatePasswordStrength()"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(false)"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<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">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(true)"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</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" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
||||
*ngIf="masterPasswordScoreText">
|
||||
{{masterPasswordScoreText}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
[appAutofocus]="email !== ''" appInputVerbatim (input)="updatePasswordStrength()">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
<div class="box">
|
||||
<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">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</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>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="!showCaptcha()"><iframe id="hcaptcha_iframe" height="80"></iframe></div>
|
||||
<div class="box last" *ngIf="showTerms">
|
||||
<div class="box-content">
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox box-content-row-checkbox-left box-content-row-word-break"
|
||||
appBoxRow
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="acceptPolicies"
|
||||
[(ngModel)]="acceptPolicies"
|
||||
name="AcceptPolicies"
|
||||
/>
|
||||
<label for="acceptPolicies">
|
||||
{{ "acceptPolicies" | i18n }}<br />
|
||||
<a href="https://bitwarden.com/terms/" target="_blank" rel="noopener">{{
|
||||
"termsOfService" | i18n
|
||||
}}</a
|
||||
>,
|
||||
<a href="https://bitwarden.com/privacy/" target="_blank" rel="noopener">{{
|
||||
"privacyPolicy" | i18n
|
||||
}}</a>
|
||||
</label>
|
||||
</div>
|
||||
<div [hidden]="!showCaptcha()"><iframe id="hcaptcha_iframe" height="80"></iframe></div>
|
||||
<div class="box last" *ngIf="showTerms">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox box-content-row-checkbox-left box-content-row-word-break"
|
||||
appBoxRow>
|
||||
<input type="checkbox" id="acceptPolicies" [(ngModel)]="acceptPolicies" name="AcceptPolicies">
|
||||
<label for="acceptPolicies">
|
||||
{{'acceptPolicies' | i18n}}<br>
|
||||
<a href="https://bitwarden.com/terms/" target="_blank"
|
||||
rel="noopener">{{'termsOfService' | i18n}}</a>,
|
||||
<a href="https://bitwarden.com/privacy/" target="_blank"
|
||||
rel="noopener">{{'privacyPolicy' | i18n}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,29 +1,46 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { RegisterComponent as BaseRegisterComponent } from 'jslib-angular/components/register.component';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
templateUrl: 'register.component.html',
|
||||
selector: "app-register",
|
||||
templateUrl: "register.component.html",
|
||||
})
|
||||
export class RegisterComponent extends BaseRegisterComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, cryptoService: CryptoService,
|
||||
apiService: ApiService, stateService: StateService, platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService, environmentService: EnvironmentService,
|
||||
logService: LogService) {
|
||||
super(authService, router, i18nService, cryptoService, apiService, stateService, platformUtilsService,
|
||||
passwordGenerationService, environmentService, logService);
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
apiService: ApiService,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
environmentService: EnvironmentService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
cryptoService,
|
||||
apiService,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
environmentService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,49 @@
|
||||
<header>
|
||||
<div class="left"></div>
|
||||
<div class="center">
|
||||
<span class="title">{{'removeMasterPassword' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right"></div>
|
||||
<div class="left"></div>
|
||||
<div class="center">
|
||||
<span class="title">{{ "removeMasterPassword" | i18n }}</span>
|
||||
</div>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<p>{{'convertOrganizationEncryptionDesc' | i18n : organization.name}}</p>
|
||||
</div>
|
||||
<div class="box-content-row">
|
||||
<button type="button" class="btn block primary" (click)="convert()" [disabled]="actionPromise">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true" *ngIf="continuing"></i>
|
||||
{{'removeMasterPassword' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content-row">
|
||||
<button type="button" class="btn btn-outline-secondary block" (click)="leave()" [disabled]="actionPromise">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true" *ngIf="leaving"></i>
|
||||
{{'leaveOrganization' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<p>{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}</p>
|
||||
</div>
|
||||
<div class="box-content-row">
|
||||
<button
|
||||
type="button"
|
||||
class="btn block primary"
|
||||
(click)="convert()"
|
||||
[disabled]="actionPromise"
|
||||
>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
*ngIf="continuing"
|
||||
></i>
|
||||
{{ "removeMasterPassword" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content-row">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary block"
|
||||
(click)="leave()"
|
||||
[disabled]="actionPromise"
|
||||
>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
*ngIf="leaving"
|
||||
></i>
|
||||
{{ "leaveOrganization" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from 'jslib-angular/components/remove-password.component';
|
||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "jslib-angular/components/remove-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-remove-password',
|
||||
templateUrl: 'remove-password.component.html',
|
||||
selector: "app-remove-password",
|
||||
templateUrl: "remove-password.component.html",
|
||||
})
|
||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {
|
||||
}
|
||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
||||
|
||||
@@ -1,100 +1,150 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'setMasterPassword' | i18n}}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="full-loading-spinner" *ngIf="syncLoading">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div *ngIf="!syncLoading">
|
||||
<div class="box">
|
||||
<app-callout type="tip">{{'ssoCompleteRegistration' | i18n}}</app-callout>
|
||||
<app-callout type="warning" title="{{'resetPasswordPolicyAutoEnroll' | i18n}}"
|
||||
*ngIf="resetPasswordAutoEnroll">
|
||||
{{'resetPasswordAutoEnrollInviteWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
||||
</app-callout>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "setMasterPassword" | i18n }}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="full-loading-spinner" *ngIf="syncLoading">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div *ngIf="!syncLoading">
|
||||
<div class="box">
|
||||
<app-callout type="tip">{{ "ssoCompleteRegistration" | i18n }}</app-callout>
|
||||
<app-callout
|
||||
type="warning"
|
||||
title="{{ 'resetPasswordPolicyAutoEnroll' | i18n }}"
|
||||
*ngIf="resetPasswordAutoEnroll"
|
||||
>
|
||||
{{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
>
|
||||
</app-callout>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword"
|
||||
>{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||
*ngIf="masterPasswordScoreText"
|
||||
>
|
||||
{{ masterPasswordScoreText }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(false)"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
||||
*ngIf="masterPasswordScoreText">
|
||||
{{masterPasswordScoreText}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
(input)="updatePasswordStrength()" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
[ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
|
||||
class="monospaced" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<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 class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="password"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(true)"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<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>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,65 +1,79 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import {
|
||||
SetPasswordComponent as BaseSetPasswordComponent,
|
||||
} from 'jslib-angular/components/set-password.component';
|
||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-set-password',
|
||||
templateUrl: 'set-password.component.html',
|
||||
selector: "app-set-password",
|
||||
templateUrl: "set-password.component.html",
|
||||
})
|
||||
export class SetPasswordComponent extends BaseSetPasswordComponent {
|
||||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
cryptoService: CryptoService, messagingService: MessagingService,
|
||||
userService: UserService, passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService, policyService: PolicyService, router: Router,
|
||||
syncService: SyncService, route: ActivatedRoute) {
|
||||
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
|
||||
platformUtilsService, policyService, router, apiService, syncService, route);
|
||||
}
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
userService: UserService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
policyService: PolicyService,
|
||||
router: Router,
|
||||
syncService: SyncService,
|
||||
route: ActivatedRoute
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
userService,
|
||||
passwordGenerationService,
|
||||
platformUtilsService,
|
||||
policyService,
|
||||
router,
|
||||
apiService,
|
||||
syncService,
|
||||
route
|
||||
);
|
||||
}
|
||||
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return 'success';
|
||||
case 3:
|
||||
return 'primary';
|
||||
case 2:
|
||||
return 'warning';
|
||||
default:
|
||||
return 'danger';
|
||||
}
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return "success";
|
||||
case 3:
|
||||
return "primary";
|
||||
case 2:
|
||||
return "warning";
|
||||
default:
|
||||
return "danger";
|
||||
}
|
||||
}
|
||||
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t('strong');
|
||||
case 3:
|
||||
return this.i18nService.t('good');
|
||||
case 2:
|
||||
return this.i18nService.t('weak');
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t('weak') : null;
|
||||
}
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t("strong");
|
||||
case 3:
|
||||
return this.i18nService.t("good");
|
||||
case 2:
|
||||
return this.i18nService.t("weak");
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,73 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component';
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
@Component({
|
||||
selector: 'app-sso',
|
||||
templateUrl: 'sso.component.html',
|
||||
selector: "app-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
})
|
||||
export class SsoComponent extends BaseSsoComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, route: ActivatedRoute,
|
||||
storageService: StorageService, stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService, apiService: ApiService,
|
||||
cryptoFunctionService: CryptoFunctionService, passwordGenerationService: PasswordGenerationService,
|
||||
syncService: SyncService, environmentService: EnvironmentService, logService: LogService,
|
||||
private vaultTimeoutService: VaultTimeoutService) {
|
||||
super(authService, router, i18nService, route, storageService, stateService, platformUtilsService,
|
||||
apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService);
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
route: ActivatedRoute,
|
||||
storageService: StorageService,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
syncService: SyncService,
|
||||
environmentService: EnvironmentService,
|
||||
logService: LogService,
|
||||
private vaultTimeoutService: VaultTimeoutService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
route,
|
||||
storageService,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
apiService,
|
||||
cryptoFunctionService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
logService
|
||||
);
|
||||
|
||||
const url = this.environmentService.getWebVaultUrl();
|
||||
const url = this.environmentService.getWebVaultUrl();
|
||||
|
||||
this.redirectUri = url + '/sso-connector.html';
|
||||
this.clientId = 'browser';
|
||||
this.redirectUri = url + "/sso-connector.html";
|
||||
this.clientId = "browser";
|
||||
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
if (await this.vaultTimeoutService.isLocked()) {
|
||||
// If the vault is unlocked then this will clear keys from memory, which we don't want to do
|
||||
BrowserApi.reloadOpenWindows();
|
||||
}
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
if (await this.vaultTimeoutService.isLocked()) {
|
||||
// If the vault is unlocked then this will clear keys from memory, which we don't want to do
|
||||
BrowserApi.reloadOpenWindows();
|
||||
}
|
||||
|
||||
const thisWindow = window.open('', '_self');
|
||||
thisWindow.close();
|
||||
};
|
||||
}
|
||||
const thisWindow = window.open("", "_self");
|
||||
thisWindow.close();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/2fa">{{'close' | i18n}}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'twoStepOptions' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
<div class="left">
|
||||
<a routerLink="/2fa">{{ "close" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "twoStepOptions" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<button type="button" 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>
|
||||
</button>
|
||||
<button type="button" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
|
||||
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<button
|
||||
type="button"
|
||||
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>
|
||||
</button>
|
||||
<button type="button" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{ "recoveryCodeTitle" | i18n }}</span>
|
||||
<span class="detail">{{ "recoveryCodeDesc" | i18n }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
|
||||
} from 'jslib-angular/components/two-factor-options.component';
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor-options',
|
||||
templateUrl: 'two-factor-options.component.html',
|
||||
selector: "app-two-factor-options",
|
||||
templateUrl: "two-factor-options.component.html",
|
||||
})
|
||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
|
||||
super(authService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService
|
||||
) {
|
||||
super(authService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
|
||||
choose(p: any) {
|
||||
super.choose(p);
|
||||
this.authService.selectedTwoFactorProviderType = p.type;
|
||||
this.router.navigate(['2fa']);
|
||||
}
|
||||
choose(p: any) {
|
||||
super.choose(p);
|
||||
this.authService.selectedTwoFactorProviderType = p.type;
|
||||
this.router.navigate(["2fa"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,106 +1,141 @@
|
||||
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/login">{{'back' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/login">{{ "back" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ title }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button
|
||||
type="submit"
|
||||
appBlurClick
|
||||
[disabled]="form.loading"
|
||||
*ngIf="
|
||||
selectedProviderType != null &&
|
||||
selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo &&
|
||||
(selectedProviderType !== providerType.WebAuthn || form.loading)
|
||||
"
|
||||
>
|
||||
<span [hidden]="form.loading">{{ "continue" | i18n }}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></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>
|
||||
<h1 class="center">
|
||||
<span class="title">{{title}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading" *ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo &&
|
||||
(selectedProviderType !== providerType.WebAuthn || form.loading)">
|
||||
<span [hidden]="form.loading">{{'continue' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<div class="content text-center">
|
||||
<p class="text-center">{{ "insertYubiKey" | i18n }}</p>
|
||||
<img src="../images/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>
|
||||
</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 class="text-center">{{'insertYubiKey' | i18n}}</p>
|
||||
<img src="../images/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.WebAuthn && !webAuthnNewTab">
|
||||
<div id="web-authn-frame"><iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe></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>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && webAuthnNewTab">
|
||||
<div class="content text-center" *ngIf="webAuthnNewTab">
|
||||
<p class="text-center">{{'webAuthnNewTab' | i18n}}</p>
|
||||
<button type="button" class="btn primary block" (click)="authWebAuthn()" appStopClick>{{'webAuthnNewTabOpen' | i18n}}</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo">
|
||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></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" *ngIf="selectedProviderType == null">
|
||||
<p class="text-center">{{'noTwoStepProviders' | i18n}}</p>
|
||||
<p class="text-center">{{'noTwoStepProviders2' | i18n}}</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && !webAuthnNewTab">
|
||||
<div id="web-authn-frame"><iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe></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 class="content no-vpad" *ngIf="selectedProviderType != null">
|
||||
<p class="text-center">
|
||||
<button type="button" appStopClick (click)="anotherMethod()">{{'useAnotherTwoStepMethod' | i18n}}</button>
|
||||
</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email" class="text-center">
|
||||
<button type="button" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise">
|
||||
{{'sendVerificationCodeEmailAgain' | i18n}}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && webAuthnNewTab">
|
||||
<div class="content text-center" *ngIf="webAuthnNewTab">
|
||||
<p class="text-center">{{ "webAuthnNewTab" | i18n }}</p>
|
||||
<button type="button" class="btn primary block" (click)="authWebAuthn()" appStopClick>
|
||||
{{ "webAuthnNewTabOpen" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo
|
||||
"
|
||||
>
|
||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></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>
|
||||
</content>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="content" *ngIf="selectedProviderType == null">
|
||||
<p class="text-center">{{ "noTwoStepProviders" | i18n }}</p>
|
||||
<p class="text-center">{{ "noTwoStepProviders2" | i18n }}</p>
|
||||
</div>
|
||||
<div class="content no-vpad" *ngIf="selectedProviderType != null">
|
||||
<p class="text-center">
|
||||
<button type="button" appStopClick (click)="anotherMethod()">
|
||||
{{ "useAnotherTwoStepMethod" | i18n }}
|
||||
</button>
|
||||
</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email" class="text-center">
|
||||
<button type="button" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise">
|
||||
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,116 +1,140 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { TwoFactorProviderType } from 'jslib-common/enums/twoFactorProviderType';
|
||||
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib-angular/components/two-factor.component';
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
const BroadcasterSubscriptionId = 'TwoFactorComponent';
|
||||
const BroadcasterSubscriptionId = "TwoFactorComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor',
|
||||
templateUrl: 'two-factor.component.html',
|
||||
selector: "app-two-factor",
|
||||
templateUrl: "two-factor.component.html",
|
||||
})
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
showNewWindowMessage = false;
|
||||
showNewWindowMessage = false;
|
||||
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService, private syncService: SyncService,
|
||||
environmentService: EnvironmentService, private broadcasterService: BroadcasterService,
|
||||
private popupUtilsService: PopupUtilsService, stateService: StateService,
|
||||
storageService: StorageService, route: ActivatedRoute, private messagingService: MessagingService,
|
||||
logService: LogService) {
|
||||
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService,
|
||||
stateService, storageService, route, logService);
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
private syncService: SyncService,
|
||||
environmentService: EnvironmentService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private popupUtilsService: PopupUtilsService,
|
||||
stateService: StateService,
|
||||
storageService: StorageService,
|
||||
route: ActivatedRoute,
|
||||
private messagingService: MessagingService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
apiService,
|
||||
platformUtilsService,
|
||||
window,
|
||||
environmentService,
|
||||
stateService,
|
||||
storageService,
|
||||
route,
|
||||
logService
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
super.successRoute = "/tabs/vault";
|
||||
this.webAuthnNewTab =
|
||||
this.platformUtilsService.isFirefox() || this.platformUtilsService.isSafari();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
if (this.route.snapshot.paramMap.has("webAuthnResponse")) {
|
||||
// WebAuthn fallback response
|
||||
this.selectedProviderType = TwoFactorProviderType.WebAuthn;
|
||||
this.token = this.route.snapshot.paramMap.get("webAuthnResponse");
|
||||
super.onSuccessfulLogin = async () => {
|
||||
this.syncService.fullSync(true);
|
||||
this.messagingService.send("reloadPopup");
|
||||
window.close();
|
||||
};
|
||||
this.remember = this.route.snapshot.paramMap.get("remember") === "true";
|
||||
await this.doSubmit();
|
||||
return;
|
||||
}
|
||||
|
||||
await super.ngOnInit();
|
||||
if (this.selectedProviderType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WebAuthn prompt appears inside the popup on linux, and requires a larger popup width
|
||||
// than usual to avoid cutting off the dialog.
|
||||
if (this.selectedProviderType === TwoFactorProviderType.WebAuthn && (await this.isLinux())) {
|
||||
document.body.classList.add("linux-webauthn");
|
||||
}
|
||||
|
||||
if (
|
||||
this.selectedProviderType === TwoFactorProviderType.Email &&
|
||||
this.popupUtilsService.inPopup(window)
|
||||
) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("popup2faCloseMessage"),
|
||||
null,
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no")
|
||||
);
|
||||
if (confirmed) {
|
||||
this.popupUtilsService.popOut(window);
|
||||
}
|
||||
}
|
||||
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
if (qParams.sso === "true") {
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
BrowserApi.reloadOpenWindows();
|
||||
const thisWindow = window.open("", "_self");
|
||||
thisWindow.close();
|
||||
return this.syncService.fullSync(true);
|
||||
};
|
||||
super.successRoute = '/tabs/vault';
|
||||
this.webAuthnNewTab = this.platformUtilsService.isFirefox() || this.platformUtilsService.isSafari();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
|
||||
if (this.selectedProviderType === TwoFactorProviderType.WebAuthn && (await this.isLinux())) {
|
||||
document.body.classList.remove("linux-webauthn");
|
||||
}
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
if (this.route.snapshot.paramMap.has('webAuthnResponse')) {
|
||||
// WebAuthn fallback response
|
||||
this.selectedProviderType = TwoFactorProviderType.WebAuthn;
|
||||
this.token = this.route.snapshot.paramMap.get('webAuthnResponse');
|
||||
super.onSuccessfulLogin = async () => {
|
||||
this.syncService.fullSync(true);
|
||||
this.messagingService.send('reloadPopup');
|
||||
window.close();
|
||||
};
|
||||
this.remember = this.route.snapshot.paramMap.get('remember') === 'true';
|
||||
await this.doSubmit();
|
||||
return;
|
||||
}
|
||||
anotherMethod() {
|
||||
this.router.navigate(["2fa-options"]);
|
||||
}
|
||||
|
||||
await super.ngOnInit();
|
||||
if (this.selectedProviderType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WebAuthn prompt appears inside the popup on linux, and requires a larger popup width
|
||||
// than usual to avoid cutting off the dialog.
|
||||
if (this.selectedProviderType === TwoFactorProviderType.WebAuthn && await this.isLinux()) {
|
||||
document.body.classList.add('linux-webauthn');
|
||||
}
|
||||
|
||||
if (this.selectedProviderType === TwoFactorProviderType.Email &&
|
||||
this.popupUtilsService.inPopup(window)) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(this.i18nService.t('popup2faCloseMessage'),
|
||||
null, this.i18nService.t('yes'), this.i18nService.t('no'));
|
||||
if (confirmed) {
|
||||
this.popupUtilsService.popOut(window);
|
||||
}
|
||||
}
|
||||
|
||||
this.route.queryParams.pipe(first()).subscribe(async qParams => {
|
||||
if (qParams.sso === 'true') {
|
||||
super.onSuccessfulLogin = () => {
|
||||
BrowserApi.reloadOpenWindows();
|
||||
const thisWindow = window.open('', '_self');
|
||||
thisWindow.close();
|
||||
return this.syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
|
||||
if (this.selectedProviderType === TwoFactorProviderType.WebAuthn && await this.isLinux()) {
|
||||
document.body.classList.remove('linux-webauthn');
|
||||
}
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
anotherMethod() {
|
||||
this.router.navigate(['2fa-options']);
|
||||
}
|
||||
|
||||
async isLinux() {
|
||||
return (await BrowserApi.getPlatformInfo()).os === 'linux';
|
||||
}
|
||||
async isLinux() {
|
||||
return (await BrowserApi.getPlatformInfo()).os === "linux";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,130 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a (click)="logOut()">{{'logOut' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a (click)="logOut()">{{ "logOut" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "updateMasterPassword" | i18n }}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<app-callout type="warning" title="{{ 'updateMasterPassword' | i18n }}">
|
||||
{{ "updateMasterPasswordWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
>
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreStyle.Color }}"
|
||||
*ngIf="masterPasswordScoreStyle.Text"
|
||||
>
|
||||
{{ masterPasswordScoreStyle.Text }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
(input)="updatePasswordStrength()"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(false)"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreStyle.Color }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreStyle.Width + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreStyle.Width }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'updateMasterPassword' | i18n}}</span>
|
||||
</h1>
|
||||
<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" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<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)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(true)"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<app-callout type="warning" title="{{'updateMasterPassword' | i18n}}">
|
||||
{{'updateMasterPasswordWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreStyle.Color}}"
|
||||
*ngIf="masterPasswordScoreStyle.Text">
|
||||
{{masterPasswordScoreStyle.Text}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
appInputVerbatim (input)="updatePasswordStrength()">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreStyle.Color}}" role="progressbar"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
[ngStyle]="{width: (masterPasswordScoreStyle.Width + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreStyle.Width}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
<div class="box">
|
||||
<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)]="masterPasswordRetype" required
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<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>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,65 +1,82 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from 'jslib-angular/components/update-temp-password.component';
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
|
||||
|
||||
interface MasterPasswordScore {
|
||||
Color: string;
|
||||
Text: string;
|
||||
Width: number;
|
||||
Color: string;
|
||||
Text: string;
|
||||
Width: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-update-temp-password',
|
||||
templateUrl: 'update-temp-password.component.html',
|
||||
selector: "app-update-temp-password",
|
||||
templateUrl: "update-temp-password.component.html",
|
||||
})
|
||||
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return {
|
||||
Color: 'bg-success',
|
||||
Text: 'strong',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
Color: 'bg-primary',
|
||||
Text: 'good',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
Color: 'bg-warning',
|
||||
Text: 'weak',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
Color: 'bg-danger',
|
||||
Text: 'weak',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
}
|
||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return {
|
||||
Color: "bg-success",
|
||||
Text: "strong",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
Color: "bg-primary",
|
||||
Text: "good",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
Color: "bg-warning",
|
||||
Text: "weak",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
Color: "bg-danger",
|
||||
Text: "weak",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
|
||||
cryptoService: CryptoService, userService: UserService,
|
||||
messagingService: MessagingService, apiService: ApiService,
|
||||
syncService: SyncService, logService: LogService) {
|
||||
super(i18nService, platformUtilsService, passwordGenerationService, policyService, cryptoService,
|
||||
userService, messagingService, apiService, syncService, logService);
|
||||
}
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
policyService: PolicyService,
|
||||
cryptoService: CryptoService,
|
||||
userService: UserService,
|
||||
messagingService: MessagingService,
|
||||
apiService: ApiService,
|
||||
syncService: SyncService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
policyService,
|
||||
cryptoService,
|
||||
userService,
|
||||
messagingService,
|
||||
apiService,
|
||||
syncService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,199 +1,213 @@
|
||||
import {
|
||||
animate,
|
||||
group,
|
||||
query,
|
||||
style,
|
||||
transition,
|
||||
trigger,
|
||||
} from '@angular/animations';
|
||||
import { animate, group, query, style, transition, trigger } from "@angular/animations";
|
||||
|
||||
import { BrowserApi } from '../browser/browserApi';
|
||||
import { BrowserApi } from "../browser/browserApi";
|
||||
|
||||
const queryShown = query(':enter, :leave', [
|
||||
style({ position: 'fixed', width: '100%', height: '100%' }),
|
||||
], { optional: true });
|
||||
const queryShown = query(
|
||||
":enter, :leave",
|
||||
[style({ position: "fixed", width: "100%", height: "100%" })],
|
||||
{
|
||||
optional: true,
|
||||
}
|
||||
);
|
||||
|
||||
// ref: https://github.com/angular/angular/issues/15477
|
||||
const queryChildRoute = query('router-outlet ~ *', [
|
||||
style({}),
|
||||
animate(1, style({})),
|
||||
], { optional: true });
|
||||
const queryChildRoute = query("router-outlet ~ *", [style({}), animate(1, style({}))], {
|
||||
optional: true,
|
||||
});
|
||||
|
||||
const speed = '0.4s';
|
||||
const speed = "0.4s";
|
||||
|
||||
export function queryTranslate(direction: string, axis: string, from: number, to: number, zIndex: number = 1000) {
|
||||
return query(':' + direction, [
|
||||
style({ transform: 'translate' + axis + '(' + from + '%)', zIndex: zIndex, boxShadow: '0 3px 2px -2px gray' }),
|
||||
animate(speed + ' ease-in-out', style({ transform: 'translate' + axis + '(' + to + '%)' })),
|
||||
], { optional: true });
|
||||
export function queryTranslate(
|
||||
direction: string,
|
||||
axis: string,
|
||||
from: number,
|
||||
to: number,
|
||||
zIndex: number = 1000
|
||||
) {
|
||||
return query(
|
||||
":" + direction,
|
||||
[
|
||||
style({
|
||||
transform: "translate" + axis + "(" + from + "%)",
|
||||
zIndex: zIndex,
|
||||
boxShadow: "0 3px 2px -2px gray",
|
||||
}),
|
||||
animate(speed + " ease-in-out", style({ transform: "translate" + axis + "(" + to + "%)" })),
|
||||
],
|
||||
{ optional: true }
|
||||
);
|
||||
}
|
||||
|
||||
export function queryTranslateX(direction: string, from: number, to: number, zIndex: number = 1000) {
|
||||
return queryTranslate(direction, 'X', from, to, zIndex);
|
||||
export function queryTranslateX(
|
||||
direction: string,
|
||||
from: number,
|
||||
to: number,
|
||||
zIndex: number = 1000
|
||||
) {
|
||||
return queryTranslate(direction, "X", from, to, zIndex);
|
||||
}
|
||||
|
||||
export function queryTranslateY(direction: string, from: number, to: number, zIndex: number = 1000) {
|
||||
return queryTranslate(direction, 'Y', from, to, zIndex);
|
||||
export function queryTranslateY(
|
||||
direction: string,
|
||||
from: number,
|
||||
to: number,
|
||||
zIndex: number = 1000
|
||||
) {
|
||||
return queryTranslate(direction, "Y", from, to, zIndex);
|
||||
}
|
||||
|
||||
const inSlideLeft = [
|
||||
queryShown,
|
||||
group([
|
||||
queryTranslateX('enter', 100, 0),
|
||||
queryTranslateX('leave', 0, -100),
|
||||
queryChildRoute,
|
||||
]),
|
||||
queryShown,
|
||||
group([queryTranslateX("enter", 100, 0), queryTranslateX("leave", 0, -100), queryChildRoute]),
|
||||
];
|
||||
|
||||
const outSlideRight = [
|
||||
queryShown,
|
||||
group([
|
||||
queryTranslateX('enter', -100, 0),
|
||||
queryTranslateX('leave', 0, 100),
|
||||
]),
|
||||
queryShown,
|
||||
group([queryTranslateX("enter", -100, 0), queryTranslateX("leave", 0, 100)]),
|
||||
];
|
||||
|
||||
const inSlideUp = [
|
||||
queryShown,
|
||||
group([
|
||||
queryTranslateY('enter', 100, 0, 1010),
|
||||
queryTranslateY('leave', 0, 0),
|
||||
queryChildRoute,
|
||||
]),
|
||||
queryShown,
|
||||
group([queryTranslateY("enter", 100, 0, 1010), queryTranslateY("leave", 0, 0), queryChildRoute]),
|
||||
];
|
||||
|
||||
const outSlideDown = [
|
||||
queryShown,
|
||||
group([
|
||||
queryTranslateY('enter', 0, 0),
|
||||
queryTranslateY('leave', 0, 100, 1010),
|
||||
]),
|
||||
queryShown,
|
||||
group([queryTranslateY("enter", 0, 0), queryTranslateY("leave", 0, 100, 1010)]),
|
||||
];
|
||||
|
||||
const inSlideDown = [
|
||||
queryShown,
|
||||
group([
|
||||
queryTranslateY('enter', -100, 0, 1010),
|
||||
queryTranslateY('leave', 0, 0),
|
||||
queryChildRoute,
|
||||
]),
|
||||
queryShown,
|
||||
group([queryTranslateY("enter", -100, 0, 1010), queryTranslateY("leave", 0, 0), queryChildRoute]),
|
||||
];
|
||||
|
||||
const outSlideUp = [
|
||||
queryShown,
|
||||
group([
|
||||
queryTranslateY('enter', 0, 0),
|
||||
queryTranslateY('leave', 0, -100, 1010),
|
||||
]),
|
||||
queryShown,
|
||||
group([queryTranslateY("enter", 0, 0), queryTranslateY("leave", 0, -100, 1010)]),
|
||||
];
|
||||
|
||||
export function tabsToCiphers(fromState: string, toState: string) {
|
||||
if (fromState == null || toState === null || toState.indexOf('ciphers_') === -1) {
|
||||
return false;
|
||||
}
|
||||
return (fromState.indexOf('ciphers_') === 0 && fromState.indexOf('ciphers_direction=b') === -1) ||
|
||||
fromState === 'tabs';
|
||||
if (fromState == null || toState === null || toState.indexOf("ciphers_") === -1) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
(fromState.indexOf("ciphers_") === 0 && fromState.indexOf("ciphers_direction=b") === -1) ||
|
||||
fromState === "tabs"
|
||||
);
|
||||
}
|
||||
|
||||
export function ciphersToTabs(fromState: string, toState: string) {
|
||||
if (fromState == null || toState === null || fromState.indexOf('ciphers_') === -1) {
|
||||
return false;
|
||||
}
|
||||
return toState.indexOf('ciphers_direction=b') === 0 || toState === 'tabs';
|
||||
if (fromState == null || toState === null || fromState.indexOf("ciphers_") === -1) {
|
||||
return false;
|
||||
}
|
||||
return toState.indexOf("ciphers_direction=b") === 0 || toState === "tabs";
|
||||
}
|
||||
|
||||
export function ciphersToView(fromState: string, toState: string) {
|
||||
if (fromState == null || toState === null) {
|
||||
return false;
|
||||
}
|
||||
return fromState.indexOf('ciphers_') === 0 &&
|
||||
(toState === 'view-cipher' || toState === 'add-cipher' || toState === 'clone-cipher');
|
||||
if (fromState == null || toState === null) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
fromState.indexOf("ciphers_") === 0 &&
|
||||
(toState === "view-cipher" || toState === "add-cipher" || toState === "clone-cipher")
|
||||
);
|
||||
}
|
||||
|
||||
export function viewToCiphers(fromState: string, toState: string) {
|
||||
if (fromState == null || toState === null) {
|
||||
return false;
|
||||
}
|
||||
return (fromState === 'view-cipher' || fromState === 'add-cipher' || fromState === 'clone-cipher') &&
|
||||
toState.indexOf('ciphers_') === 0;
|
||||
if (fromState == null || toState === null) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
(fromState === "view-cipher" || fromState === "add-cipher" || fromState === "clone-cipher") &&
|
||||
toState.indexOf("ciphers_") === 0
|
||||
);
|
||||
}
|
||||
|
||||
export const routerTransition = trigger('routerTransition', [
|
||||
transition('void => home', inSlideLeft),
|
||||
transition('void => tabs', inSlideLeft),
|
||||
export const routerTransition = trigger("routerTransition", [
|
||||
transition("void => home", inSlideLeft),
|
||||
transition("void => tabs", inSlideLeft),
|
||||
|
||||
transition('home => environment, home => login, home => register', inSlideUp),
|
||||
transition("home => environment, home => login, home => register", inSlideUp),
|
||||
|
||||
transition('login => home', outSlideDown),
|
||||
transition('login => hint', inSlideUp),
|
||||
transition('login => tabs, login => 2fa', inSlideLeft),
|
||||
transition("login => home", outSlideDown),
|
||||
transition("login => hint", inSlideUp),
|
||||
transition("login => tabs, login => 2fa", inSlideLeft),
|
||||
|
||||
transition('hint => login, register => home, environment => home', outSlideDown),
|
||||
transition("hint => login, register => home, environment => home", outSlideDown),
|
||||
|
||||
transition('2fa => login', outSlideRight),
|
||||
transition('2fa => 2fa-options', inSlideUp),
|
||||
transition('2fa-options => 2fa', outSlideDown),
|
||||
transition('2fa => tabs', inSlideLeft),
|
||||
transition("2fa => login", outSlideRight),
|
||||
transition("2fa => 2fa-options", inSlideUp),
|
||||
transition("2fa-options => 2fa", outSlideDown),
|
||||
transition("2fa => tabs", inSlideLeft),
|
||||
|
||||
transition(tabsToCiphers, inSlideLeft),
|
||||
transition(ciphersToTabs, outSlideRight),
|
||||
transition(tabsToCiphers, inSlideLeft),
|
||||
transition(ciphersToTabs, outSlideRight),
|
||||
|
||||
transition(ciphersToView, inSlideUp),
|
||||
transition(viewToCiphers, outSlideDown),
|
||||
transition(ciphersToView, inSlideUp),
|
||||
transition(viewToCiphers, outSlideDown),
|
||||
|
||||
transition('tabs => view-cipher', inSlideUp),
|
||||
transition('view-cipher => tabs', outSlideDown),
|
||||
transition("tabs => view-cipher", inSlideUp),
|
||||
transition("view-cipher => tabs", outSlideDown),
|
||||
|
||||
transition('view-cipher => edit-cipher, view-cipher => cipher-password-history', inSlideUp),
|
||||
transition('edit-cipher => view-cipher, cipher-password-history => view-cipher, edit-cipher => tabs', outSlideDown),
|
||||
transition("view-cipher => edit-cipher, view-cipher => cipher-password-history", inSlideUp),
|
||||
transition(
|
||||
"edit-cipher => view-cipher, cipher-password-history => view-cipher, edit-cipher => tabs",
|
||||
outSlideDown
|
||||
),
|
||||
|
||||
transition('view-cipher => clone-cipher', inSlideUp),
|
||||
transition('clone-cipher => view-cipher, clone-cipher => tabs', outSlideDown),
|
||||
transition("view-cipher => clone-cipher", inSlideUp),
|
||||
transition("clone-cipher => view-cipher, clone-cipher => tabs", outSlideDown),
|
||||
|
||||
transition('view-cipher => share-cipher', inSlideUp),
|
||||
transition('share-cipher => view-cipher', outSlideDown),
|
||||
transition("view-cipher => share-cipher", inSlideUp),
|
||||
transition("share-cipher => view-cipher", outSlideDown),
|
||||
|
||||
transition('tabs => add-cipher', inSlideUp),
|
||||
transition('add-cipher => tabs', outSlideDown),
|
||||
transition("tabs => add-cipher", inSlideUp),
|
||||
transition("add-cipher => tabs", outSlideDown),
|
||||
|
||||
transition('generator => generator-history, tabs => generator-history', inSlideLeft),
|
||||
transition('generator-history => generator, generator-history => tabs', outSlideRight),
|
||||
transition("generator => generator-history, tabs => generator-history", inSlideLeft),
|
||||
transition("generator-history => generator, generator-history => tabs", outSlideRight),
|
||||
|
||||
transition('add-cipher => generator, edit-cipher => generator, clone-cipher => generator', inSlideUp),
|
||||
transition('generator => add-cipher, generator => edit-cipher, generator => clone-cipher', outSlideDown),
|
||||
transition(
|
||||
"add-cipher => generator, edit-cipher => generator, clone-cipher => generator",
|
||||
inSlideUp
|
||||
),
|
||||
transition(
|
||||
"generator => add-cipher, generator => edit-cipher, generator => clone-cipher",
|
||||
outSlideDown
|
||||
),
|
||||
|
||||
transition('edit-cipher => attachments, edit-cipher => collections', inSlideLeft),
|
||||
transition('attachments => edit-cipher, collections => edit-cipher', outSlideRight),
|
||||
transition("edit-cipher => attachments, edit-cipher => collections", inSlideLeft),
|
||||
transition("attachments => edit-cipher, collections => edit-cipher", outSlideRight),
|
||||
|
||||
transition('clone-cipher => attachments, clone-cipher => collections', inSlideLeft),
|
||||
transition('attachments => clone-cipher, collections => clone-cipher', outSlideRight),
|
||||
transition("clone-cipher => attachments, clone-cipher => collections", inSlideLeft),
|
||||
transition("attachments => clone-cipher, collections => clone-cipher", outSlideRight),
|
||||
|
||||
transition('tabs => export', inSlideLeft),
|
||||
transition('export => tabs', outSlideRight),
|
||||
transition("tabs => export", inSlideLeft),
|
||||
transition("export => tabs", outSlideRight),
|
||||
|
||||
transition('tabs => folders', inSlideLeft),
|
||||
transition('folders => tabs', outSlideRight),
|
||||
transition("tabs => folders", inSlideLeft),
|
||||
transition("folders => tabs", outSlideRight),
|
||||
|
||||
transition('folders => edit-folder, folders => add-folder', inSlideUp),
|
||||
transition('edit-folder => folders, add-folder => folders', outSlideDown),
|
||||
transition("folders => edit-folder, folders => add-folder", inSlideUp),
|
||||
transition("edit-folder => folders, add-folder => folders", outSlideDown),
|
||||
|
||||
transition('tabs => sync', inSlideLeft),
|
||||
transition('sync => tabs', outSlideRight),
|
||||
transition("tabs => sync", inSlideLeft),
|
||||
transition("sync => tabs", outSlideRight),
|
||||
|
||||
transition('tabs => options', inSlideLeft),
|
||||
transition('options => tabs', outSlideRight),
|
||||
transition("tabs => options", inSlideLeft),
|
||||
transition("options => tabs", outSlideRight),
|
||||
|
||||
transition('tabs => premium', inSlideLeft),
|
||||
transition('premium => tabs', outSlideRight),
|
||||
transition("tabs => premium", inSlideLeft),
|
||||
transition("premium => tabs", outSlideRight),
|
||||
|
||||
transition('tabs => lock', inSlideDown),
|
||||
transition("tabs => lock", inSlideDown),
|
||||
|
||||
transition('tabs => send-type', inSlideLeft),
|
||||
transition('send-type => tabs', outSlideRight),
|
||||
transition("tabs => send-type", inSlideLeft),
|
||||
transition("send-type => tabs", outSlideRight),
|
||||
|
||||
transition('tabs => add-send, send-type => add-send', inSlideUp),
|
||||
transition('add-send => tabs, add-send => send-type', outSlideDown),
|
||||
transition("tabs => add-send, send-type => add-send", inSlideUp),
|
||||
transition("add-send => tabs, add-send => send-type", outSlideDown),
|
||||
|
||||
transition('tabs => edit-send, send-type => edit-send', inSlideUp),
|
||||
transition('edit-send => tabs, edit-send => send-type', outSlideDown),
|
||||
transition("tabs => edit-send, send-type => edit-send", inSlideUp),
|
||||
transition("edit-send => tabs, edit-send => send-type", outSlideDown),
|
||||
]);
|
||||
|
||||
@@ -1,355 +1,352 @@
|
||||
import { Injectable, NgModule } from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
RouteReuseStrategy,
|
||||
RouterModule,
|
||||
Routes,
|
||||
} from '@angular/router';
|
||||
import { Injectable, NgModule } from "@angular/core";
|
||||
import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuardService } from 'jslib-angular/services/auth-guard.service';
|
||||
import { LockGuardService } from 'jslib-angular/services/lock-guard.service';
|
||||
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||
import { LockGuardService } from "jslib-angular/services/lock-guard.service";
|
||||
|
||||
import { DebounceNavigationService } from './services/debounceNavigationService';
|
||||
import { LaunchGuardService } from './services/launch-guard.service';
|
||||
import { DebounceNavigationService } from "./services/debounceNavigationService";
|
||||
import { LaunchGuardService } from "./services/launch-guard.service";
|
||||
|
||||
import { EnvironmentComponent } from './accounts/environment.component';
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { HomeComponent } from './accounts/home.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
||||
import { SsoComponent } from './accounts/sso.component';
|
||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
||||
import { EnvironmentComponent } from "./accounts/environment.component";
|
||||
import { HintComponent } from "./accounts/hint.component";
|
||||
import { HomeComponent } from "./accounts/home.component";
|
||||
import { LockComponent } from "./accounts/lock.component";
|
||||
import { LoginComponent } from "./accounts/login.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
import { TwoFactorOptionsComponent } from "./accounts/two-factor-options.component";
|
||||
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||
|
||||
import { PasswordGeneratorHistoryComponent } from './generator/password-generator-history.component';
|
||||
import { PasswordGeneratorComponent } from './generator/password-generator.component';
|
||||
import { PasswordGeneratorHistoryComponent } from "./generator/password-generator-history.component";
|
||||
import { PasswordGeneratorComponent } from "./generator/password-generator.component";
|
||||
|
||||
import { PrivateModeComponent } from './private-mode.component';
|
||||
import { TabsComponent } from './tabs.component';
|
||||
import { PrivateModeComponent } from "./private-mode.component";
|
||||
import { TabsComponent } from "./tabs.component";
|
||||
|
||||
import { ExcludedDomainsComponent } from './settings/excluded-domains.component';
|
||||
import { ExportComponent } from './settings/export.component';
|
||||
import { FolderAddEditComponent } from './settings/folder-add-edit.component';
|
||||
import { FoldersComponent } from './settings/folders.component';
|
||||
import { OptionsComponent } from './settings/options.component';
|
||||
import { PremiumComponent } from './settings/premium.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { SyncComponent } from './settings/sync.component';
|
||||
import { ExcludedDomainsComponent } from "./settings/excluded-domains.component";
|
||||
import { ExportComponent } from "./settings/export.component";
|
||||
import { FolderAddEditComponent } from "./settings/folder-add-edit.component";
|
||||
import { FoldersComponent } from "./settings/folders.component";
|
||||
import { OptionsComponent } from "./settings/options.component";
|
||||
import { PremiumComponent } from "./settings/premium.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
import { SyncComponent } from "./settings/sync.component";
|
||||
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CollectionsComponent } from './vault/collections.component';
|
||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { ViewComponent } from './vault/view.component';
|
||||
import { AddEditComponent } from "./vault/add-edit.component";
|
||||
import { AttachmentsComponent } from "./vault/attachments.component";
|
||||
import { CiphersComponent } from "./vault/ciphers.component";
|
||||
import { CollectionsComponent } from "./vault/collections.component";
|
||||
import { CurrentTabComponent } from "./vault/current-tab.component";
|
||||
import { GroupingsComponent } from "./vault/groupings.component";
|
||||
import { PasswordHistoryComponent } from "./vault/password-history.component";
|
||||
import { ShareComponent } from "./vault/share.component";
|
||||
import { ViewComponent } from "./vault/view.component";
|
||||
|
||||
import { SendAddEditComponent } from './send/send-add-edit.component';
|
||||
import { SendGroupingsComponent } from './send/send-groupings.component';
|
||||
import { SendTypeComponent } from './send/send-type.component';
|
||||
import { SendAddEditComponent } from "./send/send-add-edit.component";
|
||||
import { SendGroupingsComponent } from "./send/send-groupings.component";
|
||||
import { SendTypeComponent } from "./send/send-type.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'home',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'vault',
|
||||
redirectTo: '/tabs/vault',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
component: HomeComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: 'home' },
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: 'login' },
|
||||
},
|
||||
{
|
||||
path: 'lock',
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuardService],
|
||||
data: { state: 'lock' },
|
||||
},
|
||||
{
|
||||
path: '2fa',
|
||||
component: TwoFactorComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: '2fa' },
|
||||
},
|
||||
{
|
||||
path: '2fa-options',
|
||||
component: TwoFactorOptionsComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: '2fa-options' },
|
||||
},
|
||||
{
|
||||
path: 'sso',
|
||||
component: SsoComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: 'sso' },
|
||||
},
|
||||
{
|
||||
path: 'set-password',
|
||||
component: SetPasswordComponent,
|
||||
data: { state: 'set-password' },
|
||||
},
|
||||
{
|
||||
path: 'remove-password',
|
||||
component: RemovePasswordComponent,
|
||||
{
|
||||
path: "",
|
||||
redirectTo: "home",
|
||||
pathMatch: "full",
|
||||
},
|
||||
{
|
||||
path: "vault",
|
||||
redirectTo: "/tabs/vault",
|
||||
pathMatch: "full",
|
||||
},
|
||||
{
|
||||
path: "home",
|
||||
component: HomeComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "home" },
|
||||
},
|
||||
{
|
||||
path: "login",
|
||||
component: LoginComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "login" },
|
||||
},
|
||||
{
|
||||
path: "lock",
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuardService],
|
||||
data: { state: "lock" },
|
||||
},
|
||||
{
|
||||
path: "2fa",
|
||||
component: TwoFactorComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "2fa" },
|
||||
},
|
||||
{
|
||||
path: "2fa-options",
|
||||
component: TwoFactorOptionsComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "2fa-options" },
|
||||
},
|
||||
{
|
||||
path: "sso",
|
||||
component: SsoComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "sso" },
|
||||
},
|
||||
{
|
||||
path: "set-password",
|
||||
component: SetPasswordComponent,
|
||||
data: { state: "set-password" },
|
||||
},
|
||||
{
|
||||
path: "remove-password",
|
||||
component: RemovePasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "remove-password" },
|
||||
},
|
||||
{
|
||||
path: "register",
|
||||
component: RegisterComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "register" },
|
||||
},
|
||||
{
|
||||
path: "hint",
|
||||
component: HintComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "hint" },
|
||||
},
|
||||
{
|
||||
path: "environment",
|
||||
component: EnvironmentComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: "environment" },
|
||||
},
|
||||
{
|
||||
path: "ciphers",
|
||||
component: CiphersComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "ciphers" },
|
||||
},
|
||||
{
|
||||
path: "view-cipher",
|
||||
component: ViewComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "view-cipher" },
|
||||
},
|
||||
{
|
||||
path: "cipher-password-history",
|
||||
component: PasswordHistoryComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "cipher-password-history" },
|
||||
},
|
||||
{
|
||||
path: "add-cipher",
|
||||
component: AddEditComponent,
|
||||
canActivate: [AuthGuardService, DebounceNavigationService],
|
||||
data: { state: "add-cipher" },
|
||||
runGuardsAndResolvers: "always",
|
||||
},
|
||||
{
|
||||
path: "edit-cipher",
|
||||
component: AddEditComponent,
|
||||
canActivate: [AuthGuardService, DebounceNavigationService],
|
||||
data: { state: "edit-cipher" },
|
||||
runGuardsAndResolvers: "always",
|
||||
},
|
||||
{
|
||||
path: "share-cipher",
|
||||
component: ShareComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "share-cipher" },
|
||||
},
|
||||
{
|
||||
path: "collections",
|
||||
component: CollectionsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "collections" },
|
||||
},
|
||||
{
|
||||
path: "attachments",
|
||||
component: AttachmentsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "attachments" },
|
||||
},
|
||||
{
|
||||
path: "generator",
|
||||
component: PasswordGeneratorComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "generator" },
|
||||
},
|
||||
{
|
||||
path: "generator-history",
|
||||
component: PasswordGeneratorHistoryComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "generator-history" },
|
||||
},
|
||||
{
|
||||
path: "export",
|
||||
component: ExportComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "export" },
|
||||
},
|
||||
{
|
||||
path: "folders",
|
||||
component: FoldersComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "folders" },
|
||||
},
|
||||
{
|
||||
path: "add-folder",
|
||||
component: FolderAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "add-folder" },
|
||||
},
|
||||
{
|
||||
path: "edit-folder",
|
||||
component: FolderAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "edit-folder" },
|
||||
},
|
||||
{
|
||||
path: "sync",
|
||||
component: SyncComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "sync" },
|
||||
},
|
||||
{
|
||||
path: "excluded-domains",
|
||||
component: ExcludedDomainsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "excluded-domains" },
|
||||
},
|
||||
{
|
||||
path: "premium",
|
||||
component: PremiumComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "premium" },
|
||||
},
|
||||
{
|
||||
path: "options",
|
||||
component: OptionsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "options" },
|
||||
},
|
||||
{
|
||||
path: "private-mode",
|
||||
component: PrivateModeComponent,
|
||||
data: { state: "private-mode" },
|
||||
},
|
||||
{
|
||||
path: "clone-cipher",
|
||||
component: AddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "clone-cipher" },
|
||||
},
|
||||
{
|
||||
path: "send-type",
|
||||
component: SendTypeComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "send-type" },
|
||||
},
|
||||
{
|
||||
path: "add-send",
|
||||
component: SendAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "add-send" },
|
||||
},
|
||||
{
|
||||
path: "edit-send",
|
||||
component: SendAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "edit-send" },
|
||||
},
|
||||
{
|
||||
path: "update-temp-password",
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: "update-temp-password" },
|
||||
},
|
||||
{
|
||||
path: "tabs",
|
||||
component: TabsComponent,
|
||||
data: { state: "tabs" },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
redirectTo: "/tabs/vault",
|
||||
pathMatch: "full",
|
||||
},
|
||||
{
|
||||
path: "current",
|
||||
component: CurrentTabComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'remove-password' },
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
component: RegisterComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: 'register' },
|
||||
},
|
||||
{
|
||||
path: 'hint',
|
||||
component: HintComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: 'hint' },
|
||||
},
|
||||
{
|
||||
path: 'environment',
|
||||
component: EnvironmentComponent,
|
||||
canActivate: [LaunchGuardService],
|
||||
data: { state: 'environment' },
|
||||
},
|
||||
{
|
||||
path: 'ciphers',
|
||||
component: CiphersComponent,
|
||||
data: { state: "tabs_current" },
|
||||
runGuardsAndResolvers: "always",
|
||||
},
|
||||
{
|
||||
path: "vault",
|
||||
component: GroupingsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'ciphers' },
|
||||
},
|
||||
{
|
||||
path: 'view-cipher',
|
||||
component: ViewComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'view-cipher' },
|
||||
},
|
||||
{
|
||||
path: 'cipher-password-history',
|
||||
component: PasswordHistoryComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'cipher-password-history' },
|
||||
},
|
||||
{
|
||||
path: 'add-cipher',
|
||||
component: AddEditComponent,
|
||||
canActivate: [AuthGuardService, DebounceNavigationService],
|
||||
data: { state: 'add-cipher' },
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'edit-cipher',
|
||||
component: AddEditComponent,
|
||||
canActivate: [AuthGuardService, DebounceNavigationService],
|
||||
data: { state: 'edit-cipher' },
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'share-cipher',
|
||||
component: ShareComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'share-cipher' },
|
||||
},
|
||||
{
|
||||
path: 'collections',
|
||||
component: CollectionsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'collections' },
|
||||
},
|
||||
{
|
||||
path: 'attachments',
|
||||
component: AttachmentsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'attachments' },
|
||||
},
|
||||
{
|
||||
path: 'generator',
|
||||
data: { state: "tabs_vault" },
|
||||
},
|
||||
{
|
||||
path: "generator",
|
||||
component: PasswordGeneratorComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'generator' },
|
||||
},
|
||||
{
|
||||
path: 'generator-history',
|
||||
component: PasswordGeneratorHistoryComponent,
|
||||
data: { state: "tabs_generator" },
|
||||
},
|
||||
{
|
||||
path: "settings",
|
||||
component: SettingsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'generator-history' },
|
||||
},
|
||||
{
|
||||
path: 'export',
|
||||
component: ExportComponent,
|
||||
data: { state: "tabs_settings" },
|
||||
},
|
||||
{
|
||||
path: "send",
|
||||
component: SendGroupingsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'export' },
|
||||
},
|
||||
{
|
||||
path: 'folders',
|
||||
component: FoldersComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'folders' },
|
||||
},
|
||||
{
|
||||
path: 'add-folder',
|
||||
component: FolderAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'add-folder' },
|
||||
},
|
||||
{
|
||||
path: 'edit-folder',
|
||||
component: FolderAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'edit-folder' },
|
||||
},
|
||||
{
|
||||
path: 'sync',
|
||||
component: SyncComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'sync' },
|
||||
},
|
||||
{
|
||||
path: 'excluded-domains',
|
||||
component: ExcludedDomainsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'excluded-domains' },
|
||||
},
|
||||
{
|
||||
path: 'premium',
|
||||
component: PremiumComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'premium' },
|
||||
},
|
||||
{
|
||||
path: 'options',
|
||||
component: OptionsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'options' },
|
||||
},
|
||||
{
|
||||
path: 'private-mode',
|
||||
component: PrivateModeComponent,
|
||||
data: { state: 'private-mode' },
|
||||
},
|
||||
{
|
||||
path: 'clone-cipher',
|
||||
component: AddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'clone-cipher' },
|
||||
},
|
||||
{
|
||||
path: 'send-type',
|
||||
component: SendTypeComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'send-type' },
|
||||
},
|
||||
{
|
||||
path: 'add-send',
|
||||
component: SendAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'add-send' },
|
||||
},
|
||||
{
|
||||
path: 'edit-send',
|
||||
component: SendAddEditComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'edit-send' },
|
||||
},
|
||||
{
|
||||
path: 'update-temp-password',
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'update-temp-password' },
|
||||
},
|
||||
{
|
||||
path: 'tabs',
|
||||
component: TabsComponent,
|
||||
data: { state: 'tabs' },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/tabs/vault',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'current',
|
||||
component: CurrentTabComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'tabs_current' },
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'vault',
|
||||
component: GroupingsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'tabs_vault' },
|
||||
},
|
||||
{
|
||||
path: 'generator',
|
||||
component: PasswordGeneratorComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'tabs_generator' },
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'tabs_settings' },
|
||||
},
|
||||
{
|
||||
path: 'send',
|
||||
component: SendGroupingsComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'tabs_send' },
|
||||
},
|
||||
],
|
||||
},
|
||||
data: { state: "tabs_send" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class NoRouteReuseStrategy implements RouteReuseStrategy {
|
||||
shouldDetach(route: ActivatedRouteSnapshot) {
|
||||
return false;
|
||||
}
|
||||
shouldDetach(route: ActivatedRouteSnapshot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
store(route: ActivatedRouteSnapshot, handle: {}) { /* Nothing */ }
|
||||
store(route: ActivatedRouteSnapshot, handle: {}) {
|
||||
/* Nothing */
|
||||
}
|
||||
|
||||
shouldAttach(route: ActivatedRouteSnapshot) {
|
||||
return false;
|
||||
}
|
||||
shouldAttach(route: ActivatedRouteSnapshot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
retrieve(route: ActivatedRouteSnapshot): any {
|
||||
return null;
|
||||
}
|
||||
retrieve(route: ActivatedRouteSnapshot): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) {
|
||||
return false;
|
||||
}
|
||||
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
onSameUrlNavigation: 'reload',
|
||||
/*enableTracing: true,*/
|
||||
})],
|
||||
exports: [RouterModule],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: NoRouteReuseStrategy },
|
||||
],
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
onSameUrlNavigation: "reload",
|
||||
/*enableTracing: true,*/
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
providers: [{ provide: RouteReuseStrategy, useClass: NoRouteReuseStrategy }],
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {}
|
||||
|
||||
@@ -1,243 +1,254 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
NgZone,
|
||||
OnInit,
|
||||
SecurityContext,
|
||||
} from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import {
|
||||
NavigationEnd,
|
||||
Router,
|
||||
RouterOutlet,
|
||||
} from '@angular/router';
|
||||
import {
|
||||
IndividualConfig,
|
||||
ToastrService,
|
||||
} from 'ngx-toastr';
|
||||
import Swal, { SweetAlertIcon } from 'sweetalert2/src/sweetalert2.js';
|
||||
import { BrowserApi } from '../browser/browserApi';
|
||||
import { ChangeDetectorRef, Component, NgZone, OnInit, SecurityContext } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { NavigationEnd, Router, RouterOutlet } from "@angular/router";
|
||||
import { IndividualConfig, ToastrService } from "ngx-toastr";
|
||||
import Swal, { SweetAlertIcon } from "sweetalert2/src/sweetalert2.js";
|
||||
import { BrowserApi } from "../browser/browserApi";
|
||||
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
import { routerTransition } from './app-routing.animations';
|
||||
import { routerTransition } from "./app-routing.animations";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
styles: [],
|
||||
animations: [routerTransition],
|
||||
template: `
|
||||
<main [@routerTransition]="getState(o)">
|
||||
<router-outlet #o="outlet"></router-outlet>
|
||||
</main>`,
|
||||
selector: "app-root",
|
||||
styles: [],
|
||||
animations: [routerTransition],
|
||||
template: ` <main [@routerTransition]="getState(o)">
|
||||
<router-outlet #o="outlet"></router-outlet>
|
||||
</main>`,
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
private lastActivity: number = null;
|
||||
|
||||
private lastActivity: number = null;
|
||||
constructor(
|
||||
private toastrService: ToastrService,
|
||||
private storageService: StorageService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private authService: AuthService,
|
||||
private i18nService: I18nService,
|
||||
private router: Router,
|
||||
private stateService: StateService,
|
||||
private messagingService: MessagingService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private ngZone: NgZone,
|
||||
private sanitizer: DomSanitizer,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private keyConnectoService: KeyConnectorService
|
||||
) {}
|
||||
|
||||
constructor(private toastrService: ToastrService, private storageService: StorageService,
|
||||
private broadcasterService: BroadcasterService, private authService: AuthService,
|
||||
private i18nService: I18nService, private router: Router,
|
||||
private stateService: StateService, private messagingService: MessagingService,
|
||||
private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone,
|
||||
private sanitizer: DomSanitizer, private platformUtilsService: PlatformUtilsService,
|
||||
private keyConnectoService: KeyConnectorService) { }
|
||||
ngOnInit() {
|
||||
if (BrowserApi.getBackgroundPage() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (BrowserApi.getBackgroundPage() == null) {
|
||||
return;
|
||||
}
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
window.onmousemove = () => this.recordActivity();
|
||||
window.onmousedown = () => this.recordActivity();
|
||||
window.ontouchstart = () => this.recordActivity();
|
||||
window.onclick = () => this.recordActivity();
|
||||
window.onscroll = () => this.recordActivity();
|
||||
window.onkeypress = () => this.recordActivity();
|
||||
});
|
||||
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
window.onmousemove = () => this.recordActivity();
|
||||
window.onmousedown = () => this.recordActivity();
|
||||
window.ontouchstart = () => this.recordActivity();
|
||||
window.onclick = () => this.recordActivity();
|
||||
window.onscroll = () => this.recordActivity();
|
||||
window.onkeypress = () => this.recordActivity();
|
||||
(window as any).bitwardenPopupMainMessageListener = async (
|
||||
msg: any,
|
||||
sender: any,
|
||||
sendResponse: any
|
||||
) => {
|
||||
if (msg.command === "doneLoggingOut") {
|
||||
this.ngZone.run(async () => {
|
||||
this.authService.logOut(() => {
|
||||
if (msg.expired) {
|
||||
this.showToast({
|
||||
type: "warning",
|
||||
title: this.i18nService.t("loggedOut"),
|
||||
text: this.i18nService.t("loginExpired"),
|
||||
});
|
||||
}
|
||||
this.router.navigate(["home"]);
|
||||
this.stateService.purge();
|
||||
});
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
|
||||
(window as any).bitwardenPopupMainMessageListener = async (msg: any, sender: any, sendResponse: any) => {
|
||||
if (msg.command === 'doneLoggingOut') {
|
||||
this.ngZone.run(async () => {
|
||||
this.authService.logOut(() => {
|
||||
if (msg.expired) {
|
||||
this.showToast({
|
||||
type: 'warning',
|
||||
title: this.i18nService.t('loggedOut'),
|
||||
text: this.i18nService.t('loginExpired'),
|
||||
});
|
||||
}
|
||||
this.router.navigate(['home']);
|
||||
this.stateService.purge();
|
||||
});
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
} else if (msg.command === 'authBlocked') {
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(['home']);
|
||||
});
|
||||
} else if (msg.command === 'locked') {
|
||||
this.stateService.purge();
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(['lock']);
|
||||
});
|
||||
} else if (msg.command === 'showDialog') {
|
||||
await this.showDialog(msg);
|
||||
} else if (msg.command === 'showToast') {
|
||||
this.ngZone.run(() => {
|
||||
this.showToast(msg);
|
||||
});
|
||||
} else if (msg.command === 'reloadProcess') {
|
||||
const windowReload = this.platformUtilsService.isSafari() ||
|
||||
this.platformUtilsService.isFirefox() || this.platformUtilsService.isOpera();
|
||||
if (windowReload) {
|
||||
// Wait to make sure background has reloaded first.
|
||||
window.setTimeout(() => BrowserApi.reloadExtension(window), 2000);
|
||||
}
|
||||
} else if (msg.command === 'reloadPopup') {
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(['/']);
|
||||
});
|
||||
} else if (msg.command === 'convertAccountToKeyConnector') {
|
||||
this.ngZone.run(async () => {
|
||||
await this.keyConnectoService.setConvertAccountRequired(true);
|
||||
this.router.navigate(['/remove-password']);
|
||||
});
|
||||
} else {
|
||||
msg.webExtSender = sender;
|
||||
this.broadcasterService.send(msg);
|
||||
}
|
||||
};
|
||||
|
||||
BrowserApi.messageListener('app.component', (window as any).bitwardenPopupMainMessageListener);
|
||||
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
const url = event.urlAfterRedirects || event.url || '';
|
||||
if (url.startsWith('/tabs/') && (window as any).previousPopupUrl != null &&
|
||||
(window as any).previousPopupUrl.startsWith('/tabs/')) {
|
||||
this.stateService.remove('GroupingsComponent');
|
||||
this.stateService.remove('GroupingsComponentScope');
|
||||
this.stateService.remove('CiphersComponent');
|
||||
this.stateService.remove('SendGroupingsComponent');
|
||||
this.stateService.remove('SendGroupingsComponentScope');
|
||||
this.stateService.remove('SendTypeComponent');
|
||||
}
|
||||
if (url.startsWith('/tabs/')) {
|
||||
this.stateService.remove('addEditCipherInfo');
|
||||
}
|
||||
(window as any).previousPopupUrl = url;
|
||||
|
||||
// Clear route direction after animation (400ms)
|
||||
if ((window as any).routeDirection != null) {
|
||||
window.setTimeout(() => {
|
||||
(window as any).routeDirection = null;
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
} else if (msg.command === "authBlocked") {
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(["home"]);
|
||||
});
|
||||
}
|
||||
|
||||
getState(outlet: RouterOutlet) {
|
||||
if (outlet.activatedRouteData.state === 'ciphers') {
|
||||
const routeDirection = (window as any).routeDirection != null ? (window as any).routeDirection : '';
|
||||
return 'ciphers_direction=' + routeDirection + '_' +
|
||||
(outlet.activatedRoute.queryParams as any).value.folderId + '_' +
|
||||
(outlet.activatedRoute.queryParams as any).value.collectionId;
|
||||
} else {
|
||||
return outlet.activatedRouteData.state;
|
||||
}
|
||||
}
|
||||
|
||||
private async recordActivity() {
|
||||
const now = (new Date()).getTime();
|
||||
if (this.lastActivity != null && now - this.lastActivity < 250) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastActivity = now;
|
||||
this.storageService.save(ConstantsService.lastActiveKey, now);
|
||||
}
|
||||
|
||||
private showToast(msg: any) {
|
||||
let message = '';
|
||||
|
||||
const options: Partial<IndividualConfig> = {};
|
||||
|
||||
if (typeof (msg.text) === 'string') {
|
||||
message = msg.text;
|
||||
} else if (msg.text.length === 1) {
|
||||
message = msg.text[0];
|
||||
} else {
|
||||
msg.text.forEach((t: string) =>
|
||||
message += ('<p>' + this.sanitizer.sanitize(SecurityContext.HTML, t) + '</p>'));
|
||||
options.enableHtml = true;
|
||||
}
|
||||
if (msg.options != null) {
|
||||
if (msg.options.trustedHtml === true) {
|
||||
options.enableHtml = true;
|
||||
}
|
||||
if (msg.options.timeout != null && msg.options.timeout > 0) {
|
||||
options.timeOut = msg.options.timeout;
|
||||
}
|
||||
}
|
||||
|
||||
this.toastrService.show(message, msg.title, options, 'toast-' + msg.type);
|
||||
}
|
||||
|
||||
private async showDialog(msg: any) {
|
||||
let iconClasses: string = null;
|
||||
const type = msg.type;
|
||||
if (type != null) {
|
||||
// If you add custom types to this part, the type to SweetAlertIcon cast below needs to be changed.
|
||||
switch (type) {
|
||||
case 'success':
|
||||
iconClasses = 'fa-check text-success';
|
||||
break;
|
||||
case 'warning':
|
||||
iconClasses = 'fa-warning text-warning';
|
||||
break;
|
||||
case 'error':
|
||||
iconClasses = 'fa-bolt text-danger';
|
||||
break;
|
||||
case 'info':
|
||||
iconClasses = 'fa-info-circle text-info';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const cancelText = msg.cancelText;
|
||||
const confirmText = msg.confirmText;
|
||||
const confirmed = await Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
icon: type as SweetAlertIcon, // required to be any of the SweetAlertIcons to output the iconHtml.
|
||||
iconHtml: iconClasses != null ? `<i class="swal-custom-icon fa ${iconClasses}"></i>` : undefined,
|
||||
text: msg.text,
|
||||
html: msg.html,
|
||||
titleText: msg.title,
|
||||
showCancelButton: (cancelText != null),
|
||||
cancelButtonText: cancelText,
|
||||
showConfirmButton: true,
|
||||
confirmButtonText: confirmText == null ? this.i18nService.t('ok') : confirmText,
|
||||
timer: 300000,
|
||||
} else if (msg.command === "locked") {
|
||||
this.stateService.purge();
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(["lock"]);
|
||||
});
|
||||
|
||||
this.messagingService.send('showDialogResolve', {
|
||||
dialogId: msg.dialogId,
|
||||
confirmed: confirmed.value,
|
||||
} else if (msg.command === "showDialog") {
|
||||
await this.showDialog(msg);
|
||||
} else if (msg.command === "showToast") {
|
||||
this.ngZone.run(() => {
|
||||
this.showToast(msg);
|
||||
});
|
||||
} else if (msg.command === "reloadProcess") {
|
||||
const windowReload =
|
||||
this.platformUtilsService.isSafari() ||
|
||||
this.platformUtilsService.isFirefox() ||
|
||||
this.platformUtilsService.isOpera();
|
||||
if (windowReload) {
|
||||
// Wait to make sure background has reloaded first.
|
||||
window.setTimeout(() => BrowserApi.reloadExtension(window), 2000);
|
||||
}
|
||||
} else if (msg.command === "reloadPopup") {
|
||||
this.ngZone.run(() => {
|
||||
this.router.navigate(["/"]);
|
||||
});
|
||||
} else if (msg.command === "convertAccountToKeyConnector") {
|
||||
this.ngZone.run(async () => {
|
||||
await this.keyConnectoService.setConvertAccountRequired(true);
|
||||
this.router.navigate(["/remove-password"]);
|
||||
});
|
||||
} else {
|
||||
msg.webExtSender = sender;
|
||||
this.broadcasterService.send(msg);
|
||||
}
|
||||
};
|
||||
|
||||
BrowserApi.messageListener("app.component", (window as any).bitwardenPopupMainMessageListener);
|
||||
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
const url = event.urlAfterRedirects || event.url || "";
|
||||
if (
|
||||
url.startsWith("/tabs/") &&
|
||||
(window as any).previousPopupUrl != null &&
|
||||
(window as any).previousPopupUrl.startsWith("/tabs/")
|
||||
) {
|
||||
this.stateService.remove("GroupingsComponent");
|
||||
this.stateService.remove("GroupingsComponentScope");
|
||||
this.stateService.remove("CiphersComponent");
|
||||
this.stateService.remove("SendGroupingsComponent");
|
||||
this.stateService.remove("SendGroupingsComponentScope");
|
||||
this.stateService.remove("SendTypeComponent");
|
||||
}
|
||||
if (url.startsWith("/tabs/")) {
|
||||
this.stateService.remove("addEditCipherInfo");
|
||||
}
|
||||
(window as any).previousPopupUrl = url;
|
||||
|
||||
// Clear route direction after animation (400ms)
|
||||
if ((window as any).routeDirection != null) {
|
||||
window.setTimeout(() => {
|
||||
(window as any).routeDirection = null;
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getState(outlet: RouterOutlet) {
|
||||
if (outlet.activatedRouteData.state === "ciphers") {
|
||||
const routeDirection =
|
||||
(window as any).routeDirection != null ? (window as any).routeDirection : "";
|
||||
return (
|
||||
"ciphers_direction=" +
|
||||
routeDirection +
|
||||
"_" +
|
||||
(outlet.activatedRoute.queryParams as any).value.folderId +
|
||||
"_" +
|
||||
(outlet.activatedRoute.queryParams as any).value.collectionId
|
||||
);
|
||||
} else {
|
||||
return outlet.activatedRouteData.state;
|
||||
}
|
||||
}
|
||||
|
||||
private async recordActivity() {
|
||||
const now = new Date().getTime();
|
||||
if (this.lastActivity != null && now - this.lastActivity < 250) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastActivity = now;
|
||||
this.storageService.save(ConstantsService.lastActiveKey, now);
|
||||
}
|
||||
|
||||
private showToast(msg: any) {
|
||||
let message = "";
|
||||
|
||||
const options: Partial<IndividualConfig> = {};
|
||||
|
||||
if (typeof msg.text === "string") {
|
||||
message = msg.text;
|
||||
} else if (msg.text.length === 1) {
|
||||
message = msg.text[0];
|
||||
} else {
|
||||
msg.text.forEach(
|
||||
(t: string) =>
|
||||
(message += "<p>" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "</p>")
|
||||
);
|
||||
options.enableHtml = true;
|
||||
}
|
||||
if (msg.options != null) {
|
||||
if (msg.options.trustedHtml === true) {
|
||||
options.enableHtml = true;
|
||||
}
|
||||
if (msg.options.timeout != null && msg.options.timeout > 0) {
|
||||
options.timeOut = msg.options.timeout;
|
||||
}
|
||||
}
|
||||
|
||||
this.toastrService.show(message, msg.title, options, "toast-" + msg.type);
|
||||
}
|
||||
|
||||
private async showDialog(msg: any) {
|
||||
let iconClasses: string = null;
|
||||
const type = msg.type;
|
||||
if (type != null) {
|
||||
// If you add custom types to this part, the type to SweetAlertIcon cast below needs to be changed.
|
||||
switch (type) {
|
||||
case "success":
|
||||
iconClasses = "fa-check text-success";
|
||||
break;
|
||||
case "warning":
|
||||
iconClasses = "fa-warning text-warning";
|
||||
break;
|
||||
case "error":
|
||||
iconClasses = "fa-bolt text-danger";
|
||||
break;
|
||||
case "info":
|
||||
iconClasses = "fa-info-circle text-info";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const cancelText = msg.cancelText;
|
||||
const confirmText = msg.confirmText;
|
||||
const confirmed = await Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
icon: type as SweetAlertIcon, // required to be any of the SweetAlertIcons to output the iconHtml.
|
||||
iconHtml:
|
||||
iconClasses != null ? `<i class="swal-custom-icon fa ${iconClasses}"></i>` : undefined,
|
||||
text: msg.text,
|
||||
html: msg.html,
|
||||
titleText: msg.title,
|
||||
showCancelButton: cancelText != null,
|
||||
cancelButtonText: cancelText,
|
||||
showConfirmButton: true,
|
||||
confirmButtonText: confirmText == null ? this.i18nService.t("ok") : confirmText,
|
||||
timer: 300000,
|
||||
});
|
||||
|
||||
this.messagingService.send("showDialogResolve", {
|
||||
dialogId: msg.dialogId,
|
||||
confirmed: confirmed.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,272 +1,265 @@
|
||||
import { A11yModule } from '@angular/cdk/a11y';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { A11yModule } from "@angular/cdk/a11y";
|
||||
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { ServicesModule } from './services/services.module';
|
||||
import { AppRoutingModule } from "./app-routing.module";
|
||||
import { ServicesModule } from "./services/services.module";
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { BrowserModule } from "@angular/platform-browser";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
|
||||
import { EnvironmentComponent } from './accounts/environment.component';
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { HomeComponent } from './accounts/home.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
||||
import { SsoComponent } from './accounts/sso.component';
|
||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
||||
import { EnvironmentComponent } from "./accounts/environment.component";
|
||||
import { HintComponent } from "./accounts/hint.component";
|
||||
import { HomeComponent } from "./accounts/home.component";
|
||||
import { LockComponent } from "./accounts/lock.component";
|
||||
import { LoginComponent } from "./accounts/login.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
import { TwoFactorOptionsComponent } from "./accounts/two-factor-options.component";
|
||||
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||
|
||||
import { PasswordGeneratorHistoryComponent } from './generator/password-generator-history.component';
|
||||
import { PasswordGeneratorComponent } from './generator/password-generator.component';
|
||||
import { PasswordGeneratorHistoryComponent } from "./generator/password-generator-history.component";
|
||||
import { PasswordGeneratorComponent } from "./generator/password-generator.component";
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { PrivateModeComponent } from './private-mode.component';
|
||||
import { TabsComponent } from './tabs.component';
|
||||
import { AppComponent } from "./app.component";
|
||||
import { PrivateModeComponent } from "./private-mode.component";
|
||||
import { TabsComponent } from "./tabs.component";
|
||||
|
||||
import { ExcludedDomainsComponent } from './settings/excluded-domains.component';
|
||||
import { ExportComponent } from './settings/export.component';
|
||||
import { FolderAddEditComponent } from './settings/folder-add-edit.component';
|
||||
import { FoldersComponent } from './settings/folders.component';
|
||||
import { OptionsComponent } from './settings/options.component';
|
||||
import { PremiumComponent } from './settings/premium.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { SyncComponent } from './settings/sync.component';
|
||||
import { VaultTimeoutInputComponent } from './settings/vault-timeout-input.component';
|
||||
import { ExcludedDomainsComponent } from "./settings/excluded-domains.component";
|
||||
import { ExportComponent } from "./settings/export.component";
|
||||
import { FolderAddEditComponent } from "./settings/folder-add-edit.component";
|
||||
import { FoldersComponent } from "./settings/folders.component";
|
||||
import { OptionsComponent } from "./settings/options.component";
|
||||
import { PremiumComponent } from "./settings/premium.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
import { SyncComponent } from "./settings/sync.component";
|
||||
import { VaultTimeoutInputComponent } from "./settings/vault-timeout-input.component";
|
||||
|
||||
import { AddEditCustomFieldsComponent } from './vault/add-edit-custom-fields.component';
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CollectionsComponent } from './vault/collections.component';
|
||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { ViewCustomFieldsComponent } from './vault/view-custom-fields.component';
|
||||
import { ViewComponent } from './vault/view.component';
|
||||
import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component";
|
||||
import { AddEditComponent } from "./vault/add-edit.component";
|
||||
import { AttachmentsComponent } from "./vault/attachments.component";
|
||||
import { CiphersComponent } from "./vault/ciphers.component";
|
||||
import { CollectionsComponent } from "./vault/collections.component";
|
||||
import { CurrentTabComponent } from "./vault/current-tab.component";
|
||||
import { GroupingsComponent } from "./vault/groupings.component";
|
||||
import { PasswordHistoryComponent } from "./vault/password-history.component";
|
||||
import { ShareComponent } from "./vault/share.component";
|
||||
import { ViewCustomFieldsComponent } from "./vault/view-custom-fields.component";
|
||||
import { ViewComponent } from "./vault/view.component";
|
||||
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from './send/efflux-dates.component';
|
||||
import { SendAddEditComponent } from './send/send-add-edit.component';
|
||||
import { SendGroupingsComponent } from './send/send-groupings.component';
|
||||
import { SendTypeComponent } from './send/send-type.component';
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from "./send/efflux-dates.component";
|
||||
import { SendAddEditComponent } from "./send/send-add-edit.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 { 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 { CipherListVirtualScroll } from 'jslib-angular/directives/cipherListVirtualScroll.directive';
|
||||
import { FallbackSrcDirective } from 'jslib-angular/directives/fallback-src.directive';
|
||||
import { InputVerbatimDirective } from 'jslib-angular/directives/input-verbatim.directive';
|
||||
import { SelectCopyDirective } from 'jslib-angular/directives/select-copy.directive';
|
||||
import { StopClickDirective } from 'jslib-angular/directives/stop-click.directive';
|
||||
import { StopPropDirective } from 'jslib-angular/directives/stop-prop.directive';
|
||||
import { TrueFalseValueDirective } from 'jslib-angular/directives/true-false-value.directive';
|
||||
import { A11yTitleDirective } from "jslib-angular/directives/a11y-title.directive";
|
||||
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 { CipherListVirtualScroll } from "jslib-angular/directives/cipherListVirtualScroll.directive";
|
||||
import { FallbackSrcDirective } from "jslib-angular/directives/fallback-src.directive";
|
||||
import { InputVerbatimDirective } from "jslib-angular/directives/input-verbatim.directive";
|
||||
import { SelectCopyDirective } from "jslib-angular/directives/select-copy.directive";
|
||||
import { StopClickDirective } from "jslib-angular/directives/stop-click.directive";
|
||||
import { StopPropDirective } from "jslib-angular/directives/stop-prop.directive";
|
||||
import { TrueFalseValueDirective } from "jslib-angular/directives/true-false-value.directive";
|
||||
|
||||
import { ColorPasswordPipe } from 'jslib-angular/pipes/color-password.pipe';
|
||||
import { I18nPipe } from 'jslib-angular/pipes/i18n.pipe';
|
||||
import { SearchCiphersPipe } from 'jslib-angular/pipes/search-ciphers.pipe';
|
||||
import { ColorPasswordPipe } from "jslib-angular/pipes/color-password.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 { CipherRowComponent } from './components/cipher-row.component';
|
||||
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
||||
import { PopOutComponent } from './components/pop-out.component';
|
||||
import { SendListComponent } from './components/send-list.component';
|
||||
import { SetPinComponent } from './components/set-pin.component';
|
||||
import { VerifyMasterPasswordComponent } from './components/verify-master-password.component';
|
||||
import { ActionButtonsComponent } from "./components/action-buttons.component";
|
||||
import { CipherRowComponent } from "./components/cipher-row.component";
|
||||
import { PasswordRepromptComponent } from "./components/password-reprompt.component";
|
||||
import { PopOutComponent } from "./components/pop-out.component";
|
||||
import { SendListComponent } from "./components/send-list.component";
|
||||
import { SetPinComponent } from "./components/set-pin.component";
|
||||
import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component";
|
||||
|
||||
import { CalloutComponent } from 'jslib-angular/components/callout.component';
|
||||
import { IconComponent } from 'jslib-angular/components/icon.component';
|
||||
import { BitwardenToastModule } from 'jslib-angular/components/toastr.component';
|
||||
import { CalloutComponent } from "jslib-angular/components/callout.component";
|
||||
import { IconComponent } from "jslib-angular/components/icon.component";
|
||||
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
||||
|
||||
import {
|
||||
CurrencyPipe,
|
||||
DatePipe,
|
||||
registerLocaleData,
|
||||
} from '@angular/common';
|
||||
import localeAz from '@angular/common/locales/az';
|
||||
import localeBe from '@angular/common/locales/be';
|
||||
import localeBg from '@angular/common/locales/bg';
|
||||
import localeBn from '@angular/common/locales/bn';
|
||||
import localeCa from '@angular/common/locales/ca';
|
||||
import localeCs from '@angular/common/locales/cs';
|
||||
import localeDa from '@angular/common/locales/da';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
import localeEl from '@angular/common/locales/el';
|
||||
import localeEnGb from '@angular/common/locales/en-GB';
|
||||
import localeEnIn from '@angular/common/locales/en-IN';
|
||||
import localeEs from '@angular/common/locales/es';
|
||||
import localeEt from '@angular/common/locales/et';
|
||||
import localeFa from '@angular/common/locales/fa';
|
||||
import localeFi from '@angular/common/locales/fi';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import localeHe from '@angular/common/locales/he';
|
||||
import localeHr from '@angular/common/locales/hr';
|
||||
import localeHu from '@angular/common/locales/hu';
|
||||
import localeId from '@angular/common/locales/id';
|
||||
import localeIt from '@angular/common/locales/it';
|
||||
import localeJa from '@angular/common/locales/ja';
|
||||
import localeKn from '@angular/common/locales/kn';
|
||||
import localeKo from '@angular/common/locales/ko';
|
||||
import localeLv from '@angular/common/locales/lv';
|
||||
import localeMl from '@angular/common/locales/ml';
|
||||
import localeNb from '@angular/common/locales/nb';
|
||||
import localeNl from '@angular/common/locales/nl';
|
||||
import localePl from '@angular/common/locales/pl';
|
||||
import localePtBr from '@angular/common/locales/pt';
|
||||
import localePtPt from '@angular/common/locales/pt-PT';
|
||||
import localeRo from '@angular/common/locales/ro';
|
||||
import localeRu from '@angular/common/locales/ru';
|
||||
import localeSk from '@angular/common/locales/sk';
|
||||
import localeSr from '@angular/common/locales/sr';
|
||||
import localeSv from '@angular/common/locales/sv';
|
||||
import localeTh from '@angular/common/locales/th';
|
||||
import localeTr from '@angular/common/locales/tr';
|
||||
import localeUk from '@angular/common/locales/uk';
|
||||
import localeVi from '@angular/common/locales/vi';
|
||||
import localeZhCn from '@angular/common/locales/zh-Hans';
|
||||
import localeZhTw from '@angular/common/locales/zh-Hant';
|
||||
import { CurrencyPipe, DatePipe, registerLocaleData } from "@angular/common";
|
||||
import localeAz from "@angular/common/locales/az";
|
||||
import localeBe from "@angular/common/locales/be";
|
||||
import localeBg from "@angular/common/locales/bg";
|
||||
import localeBn from "@angular/common/locales/bn";
|
||||
import localeCa from "@angular/common/locales/ca";
|
||||
import localeCs from "@angular/common/locales/cs";
|
||||
import localeDa from "@angular/common/locales/da";
|
||||
import localeDe from "@angular/common/locales/de";
|
||||
import localeEl from "@angular/common/locales/el";
|
||||
import localeEnGb from "@angular/common/locales/en-GB";
|
||||
import localeEnIn from "@angular/common/locales/en-IN";
|
||||
import localeEs from "@angular/common/locales/es";
|
||||
import localeEt from "@angular/common/locales/et";
|
||||
import localeFa from "@angular/common/locales/fa";
|
||||
import localeFi from "@angular/common/locales/fi";
|
||||
import localeFr from "@angular/common/locales/fr";
|
||||
import localeHe from "@angular/common/locales/he";
|
||||
import localeHr from "@angular/common/locales/hr";
|
||||
import localeHu from "@angular/common/locales/hu";
|
||||
import localeId from "@angular/common/locales/id";
|
||||
import localeIt from "@angular/common/locales/it";
|
||||
import localeJa from "@angular/common/locales/ja";
|
||||
import localeKn from "@angular/common/locales/kn";
|
||||
import localeKo from "@angular/common/locales/ko";
|
||||
import localeLv from "@angular/common/locales/lv";
|
||||
import localeMl from "@angular/common/locales/ml";
|
||||
import localeNb from "@angular/common/locales/nb";
|
||||
import localeNl from "@angular/common/locales/nl";
|
||||
import localePl from "@angular/common/locales/pl";
|
||||
import localePtBr from "@angular/common/locales/pt";
|
||||
import localePtPt from "@angular/common/locales/pt-PT";
|
||||
import localeRo from "@angular/common/locales/ro";
|
||||
import localeRu from "@angular/common/locales/ru";
|
||||
import localeSk from "@angular/common/locales/sk";
|
||||
import localeSr from "@angular/common/locales/sr";
|
||||
import localeSv from "@angular/common/locales/sv";
|
||||
import localeTh from "@angular/common/locales/th";
|
||||
import localeTr from "@angular/common/locales/tr";
|
||||
import localeUk from "@angular/common/locales/uk";
|
||||
import localeVi from "@angular/common/locales/vi";
|
||||
import localeZhCn from "@angular/common/locales/zh-Hans";
|
||||
import localeZhTw from "@angular/common/locales/zh-Hant";
|
||||
|
||||
registerLocaleData(localeAz, 'az');
|
||||
registerLocaleData(localeBe, 'be');
|
||||
registerLocaleData(localeBg, 'bg');
|
||||
registerLocaleData(localeBn, 'bn');
|
||||
registerLocaleData(localeCa, 'ca');
|
||||
registerLocaleData(localeCs, 'cs');
|
||||
registerLocaleData(localeDa, 'da');
|
||||
registerLocaleData(localeDe, 'de');
|
||||
registerLocaleData(localeEl, 'el');
|
||||
registerLocaleData(localeEnGb, 'en-GB');
|
||||
registerLocaleData(localeEnIn, 'en-IN');
|
||||
registerLocaleData(localeEs, 'es');
|
||||
registerLocaleData(localeEt, 'et');
|
||||
registerLocaleData(localeFa, 'fa');
|
||||
registerLocaleData(localeFi, 'fi');
|
||||
registerLocaleData(localeFr, 'fr');
|
||||
registerLocaleData(localeHe, 'he');
|
||||
registerLocaleData(localeHr, 'hr');
|
||||
registerLocaleData(localeHu, 'hu');
|
||||
registerLocaleData(localeId, 'id');
|
||||
registerLocaleData(localeIt, 'it');
|
||||
registerLocaleData(localeJa, 'ja');
|
||||
registerLocaleData(localeKo, 'ko');
|
||||
registerLocaleData(localeKn, 'kn');
|
||||
registerLocaleData(localeLv, 'lv');
|
||||
registerLocaleData(localeMl, 'ml');
|
||||
registerLocaleData(localeNb, 'nb');
|
||||
registerLocaleData(localeNl, 'nl');
|
||||
registerLocaleData(localePl, 'pl');
|
||||
registerLocaleData(localePtBr, 'pt-BR');
|
||||
registerLocaleData(localePtPt, 'pt-PT');
|
||||
registerLocaleData(localeRo, 'ro');
|
||||
registerLocaleData(localeRu, 'ru');
|
||||
registerLocaleData(localeSk, 'sk');
|
||||
registerLocaleData(localeSr, 'sr');
|
||||
registerLocaleData(localeSv, 'sv');
|
||||
registerLocaleData(localeTh, 'th');
|
||||
registerLocaleData(localeTr, 'tr');
|
||||
registerLocaleData(localeUk, 'uk');
|
||||
registerLocaleData(localeVi, 'vi');
|
||||
registerLocaleData(localeZhCn, 'zh-CN');
|
||||
registerLocaleData(localeZhTw, 'zh-TW');
|
||||
registerLocaleData(localeAz, "az");
|
||||
registerLocaleData(localeBe, "be");
|
||||
registerLocaleData(localeBg, "bg");
|
||||
registerLocaleData(localeBn, "bn");
|
||||
registerLocaleData(localeCa, "ca");
|
||||
registerLocaleData(localeCs, "cs");
|
||||
registerLocaleData(localeDa, "da");
|
||||
registerLocaleData(localeDe, "de");
|
||||
registerLocaleData(localeEl, "el");
|
||||
registerLocaleData(localeEnGb, "en-GB");
|
||||
registerLocaleData(localeEnIn, "en-IN");
|
||||
registerLocaleData(localeEs, "es");
|
||||
registerLocaleData(localeEt, "et");
|
||||
registerLocaleData(localeFa, "fa");
|
||||
registerLocaleData(localeFi, "fi");
|
||||
registerLocaleData(localeFr, "fr");
|
||||
registerLocaleData(localeHe, "he");
|
||||
registerLocaleData(localeHr, "hr");
|
||||
registerLocaleData(localeHu, "hu");
|
||||
registerLocaleData(localeId, "id");
|
||||
registerLocaleData(localeIt, "it");
|
||||
registerLocaleData(localeJa, "ja");
|
||||
registerLocaleData(localeKo, "ko");
|
||||
registerLocaleData(localeKn, "kn");
|
||||
registerLocaleData(localeLv, "lv");
|
||||
registerLocaleData(localeMl, "ml");
|
||||
registerLocaleData(localeNb, "nb");
|
||||
registerLocaleData(localeNl, "nl");
|
||||
registerLocaleData(localePl, "pl");
|
||||
registerLocaleData(localePtBr, "pt-BR");
|
||||
registerLocaleData(localePtPt, "pt-PT");
|
||||
registerLocaleData(localeRo, "ro");
|
||||
registerLocaleData(localeRu, "ru");
|
||||
registerLocaleData(localeSk, "sk");
|
||||
registerLocaleData(localeSr, "sr");
|
||||
registerLocaleData(localeSv, "sv");
|
||||
registerLocaleData(localeTh, "th");
|
||||
registerLocaleData(localeTr, "tr");
|
||||
registerLocaleData(localeUk, "uk");
|
||||
registerLocaleData(localeVi, "vi");
|
||||
registerLocaleData(localeZhCn, "zh-CN");
|
||||
registerLocaleData(localeZhTw, "zh-TW");
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
A11yModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ScrollingModule,
|
||||
ServicesModule,
|
||||
BitwardenToastModule.forRoot({
|
||||
maxOpened: 2,
|
||||
autoDismiss: true,
|
||||
closeButton: true,
|
||||
positionClass: 'toast-bottom-full-width',
|
||||
}),
|
||||
],
|
||||
declarations: [
|
||||
A11yTitleDirective,
|
||||
ActionButtonsComponent,
|
||||
AddEditComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AttachmentsComponent,
|
||||
AutofocusDirective,
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CalloutComponent,
|
||||
CipherListVirtualScroll,
|
||||
CipherRowComponent,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
ColorPasswordPipe,
|
||||
CurrentTabComponent,
|
||||
EnvironmentComponent,
|
||||
ExcludedDomainsComponent,
|
||||
ExportComponent,
|
||||
FallbackSrcDirective,
|
||||
FolderAddEditComponent,
|
||||
FoldersComponent,
|
||||
GroupingsComponent,
|
||||
HintComponent,
|
||||
HomeComponent,
|
||||
I18nPipe,
|
||||
IconComponent,
|
||||
InputVerbatimDirective,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
OptionsComponent,
|
||||
PasswordGeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PasswordHistoryComponent,
|
||||
PasswordRepromptComponent,
|
||||
PopOutComponent,
|
||||
PremiumComponent,
|
||||
PrivateModeComponent,
|
||||
RegisterComponent,
|
||||
SearchCiphersPipe,
|
||||
SelectCopyDirective,
|
||||
SendAddEditComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SendGroupingsComponent,
|
||||
SendListComponent,
|
||||
SendTypeComponent,
|
||||
SetPasswordComponent,
|
||||
SetPinComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
SsoComponent,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
SyncComponent,
|
||||
TabsComponent,
|
||||
TrueFalseValueDirective,
|
||||
TwoFactorComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyMasterPasswordComponent,
|
||||
ViewComponent,
|
||||
ViewCustomFieldsComponent,
|
||||
RemovePasswordComponent,
|
||||
],
|
||||
entryComponents: [],
|
||||
providers: [
|
||||
CurrencyPipe,
|
||||
DatePipe,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [
|
||||
A11yModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ScrollingModule,
|
||||
ServicesModule,
|
||||
BitwardenToastModule.forRoot({
|
||||
maxOpened: 2,
|
||||
autoDismiss: true,
|
||||
closeButton: true,
|
||||
positionClass: "toast-bottom-full-width",
|
||||
}),
|
||||
],
|
||||
declarations: [
|
||||
A11yTitleDirective,
|
||||
ActionButtonsComponent,
|
||||
AddEditComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AttachmentsComponent,
|
||||
AutofocusDirective,
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CalloutComponent,
|
||||
CipherListVirtualScroll,
|
||||
CipherRowComponent,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
ColorPasswordPipe,
|
||||
CurrentTabComponent,
|
||||
EnvironmentComponent,
|
||||
ExcludedDomainsComponent,
|
||||
ExportComponent,
|
||||
FallbackSrcDirective,
|
||||
FolderAddEditComponent,
|
||||
FoldersComponent,
|
||||
GroupingsComponent,
|
||||
HintComponent,
|
||||
HomeComponent,
|
||||
I18nPipe,
|
||||
IconComponent,
|
||||
InputVerbatimDirective,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
OptionsComponent,
|
||||
PasswordGeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PasswordHistoryComponent,
|
||||
PasswordRepromptComponent,
|
||||
PopOutComponent,
|
||||
PremiumComponent,
|
||||
PrivateModeComponent,
|
||||
RegisterComponent,
|
||||
SearchCiphersPipe,
|
||||
SelectCopyDirective,
|
||||
SendAddEditComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SendGroupingsComponent,
|
||||
SendListComponent,
|
||||
SendTypeComponent,
|
||||
SetPasswordComponent,
|
||||
SetPinComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
SsoComponent,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
SyncComponent,
|
||||
TabsComponent,
|
||||
TrueFalseValueDirective,
|
||||
TwoFactorComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyMasterPasswordComponent,
|
||||
ViewComponent,
|
||||
ViewCustomFieldsComponent,
|
||||
RemovePasswordComponent,
|
||||
],
|
||||
entryComponents: [],
|
||||
providers: [CurrencyPipe, DatePipe],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {}
|
||||
|
||||
@@ -1,41 +1,87 @@
|
||||
<span class="row-btn" (click)="view()" appStopClick appStopProp appA11yTitle="{{'view' | i18n}}" *ngIf="showView">
|
||||
<i class="fa fa-lg fa-list-alt" aria-hidden="true"></i>
|
||||
<span
|
||||
class="row-btn"
|
||||
(click)="view()"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'view' | i18n }}"
|
||||
*ngIf="showView"
|
||||
>
|
||||
<i class="fa fa-lg fa-list-alt" aria-hidden="true"></i>
|
||||
</span>
|
||||
<ng-container *ngIf="cipher.type === cipherType.Login">
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'launch' | i18n}}" (click)="launchCipher()"
|
||||
*ngIf="!showView" [ngClass]="{disabled: !cipher.login.canLaunch}">
|
||||
<i class="fa fa-lg fa-share-square-o" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copyUsername' | i18n}}"
|
||||
(click)="copy(cipher, cipher.login.username, 'username', 'Username')"
|
||||
[ngClass]="{disabled: !cipher.login.username}">
|
||||
<i class="fa fa-lg fa-user" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copyPassword' | i18n}}"
|
||||
(click)="copy(cipher, cipher.login.password, 'password', 'Password')"
|
||||
[ngClass]="{disabled: (!cipher.login.password || !cipher.viewPassword)}">
|
||||
<i class="fa fa-lg fa-key" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copyVerificationCode' | i18n}}"
|
||||
(click)="copy(cipher, cipher.login.totp, 'verificationCodeTotp', 'TOTP')"
|
||||
[ngClass]="{disabled: (!displayTotpCopyButton(cipher))}">
|
||||
<i class="fa fa-lg fa-clock-o" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'launch' | i18n }}"
|
||||
(click)="launchCipher()"
|
||||
*ngIf="!showView"
|
||||
[ngClass]="{ disabled: !cipher.login.canLaunch }"
|
||||
>
|
||||
<i class="fa fa-lg fa-share-square-o" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||
(click)="copy(cipher, cipher.login.username, 'username', 'Username')"
|
||||
[ngClass]="{ disabled: !cipher.login.username }"
|
||||
>
|
||||
<i class="fa fa-lg fa-user" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(cipher, cipher.login.password, 'password', 'Password')"
|
||||
[ngClass]="{ disabled: !cipher.login.password || !cipher.viewPassword }"
|
||||
>
|
||||
<i class="fa fa-lg fa-key" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copyVerificationCode' | i18n }}"
|
||||
(click)="copy(cipher, cipher.login.totp, 'verificationCodeTotp', 'TOTP')"
|
||||
[ngClass]="{ disabled: !displayTotpCopyButton(cipher) }"
|
||||
>
|
||||
<i class="fa fa-lg fa-clock-o" aria-hidden="true"></i>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="cipher.type === cipherType.Card">
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copyNumber' | i18n}}"
|
||||
(click)="copy(cipher, cipher.card.number, 'number', 'Card Number')" [ngClass]="{disabled: !cipher.card.number}">
|
||||
<i class="fa fa-lg fa-hashtag" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copySecurityCode' | i18n}}"
|
||||
(click)="copy(cipher, cipher.card.code, 'securityCode', 'Security Code')"
|
||||
[ngClass]="{disabled: !cipher.card.code}">
|
||||
<i class="fa fa-lg fa-key" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copyNumber' | i18n }}"
|
||||
(click)="copy(cipher, cipher.card.number, 'number', 'Card Number')"
|
||||
[ngClass]="{ disabled: !cipher.card.number }"
|
||||
>
|
||||
<i class="fa fa-lg fa-hashtag" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copySecurityCode' | i18n }}"
|
||||
(click)="copy(cipher, cipher.card.code, 'securityCode', 'Security Code')"
|
||||
[ngClass]="{ disabled: !cipher.card.code }"
|
||||
>
|
||||
<i class="fa fa-lg fa-key" aria-hidden="true"></i>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="cipher.type === cipherType.SecureNote">
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copyNote' | i18n}}"
|
||||
(click)="copy(cipher, cipher.notes, 'note', 'Note')" [ngClass]="{disabled: !cipher.notes}">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copyNote' | i18n }}"
|
||||
(click)="copy(cipher, cipher.notes, 'note', 'Note')"
|
||||
[ngClass]="{ disabled: !cipher.notes }"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,82 +1,88 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { CipherRepromptType } from 'jslib-common/enums/cipherRepromptType';
|
||||
import { CipherType } from 'jslib-common/enums/cipherType';
|
||||
import { EventType } from 'jslib-common/enums/eventType';
|
||||
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
|
||||
import { CipherType } from "jslib-common/enums/cipherType";
|
||||
import { EventType } from "jslib-common/enums/eventType";
|
||||
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-action-buttons',
|
||||
templateUrl: 'action-buttons.component.html',
|
||||
selector: "app-action-buttons",
|
||||
templateUrl: "action-buttons.component.html",
|
||||
})
|
||||
export class ActionButtonsComponent {
|
||||
@Output() onView = new EventEmitter<CipherView>();
|
||||
@Output() launchEvent = new EventEmitter<CipherView>();
|
||||
@Input() cipher: CipherView;
|
||||
@Input() showView = false;
|
||||
@Output() onView = new EventEmitter<CipherView>();
|
||||
@Output() launchEvent = new EventEmitter<CipherView>();
|
||||
@Input() cipher: CipherView;
|
||||
@Input() showView = false;
|
||||
|
||||
cipherType = CipherType;
|
||||
userHasPremiumAccess = false;
|
||||
cipherType = CipherType;
|
||||
userHasPremiumAccess = false;
|
||||
|
||||
constructor(private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService, private eventService: EventService,
|
||||
private totpService: TotpService, private userService: UserService,
|
||||
private passwordRepromptService: PasswordRepromptService) { }
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private eventService: EventService,
|
||||
private totpService: TotpService,
|
||||
private userService: UserService,
|
||||
private passwordRepromptService: PasswordRepromptService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.userHasPremiumAccess = await this.userService.canAccessPremium();
|
||||
async ngOnInit() {
|
||||
this.userHasPremiumAccess = await this.userService.canAccessPremium();
|
||||
}
|
||||
|
||||
launchCipher() {
|
||||
this.launchEvent.emit(this.cipher);
|
||||
}
|
||||
|
||||
async copy(cipher: CipherView, value: string, typeI18nKey: string, aType: string) {
|
||||
if (
|
||||
this.cipher.reprompt !== CipherRepromptType.None &&
|
||||
this.passwordRepromptService.protectedFields().includes(aType) &&
|
||||
!(await this.passwordRepromptService.showPasswordPrompt())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
launchCipher() {
|
||||
this.launchEvent.emit(this.cipher);
|
||||
if (value == null || (aType === "TOTP" && !this.displayTotpCopyButton(cipher))) {
|
||||
return;
|
||||
} else if (value === cipher.login.totp) {
|
||||
value = await this.totpService.getCode(value);
|
||||
}
|
||||
|
||||
async copy(cipher: CipherView, value: string, typeI18nKey: string, aType: string) {
|
||||
if (this.cipher.reprompt !== CipherRepromptType.None && this.passwordRepromptService.protectedFields().includes(aType) &&
|
||||
!await this.passwordRepromptService.showPasswordPrompt()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null || aType === 'TOTP' && !this.displayTotpCopyButton(cipher)) {
|
||||
return;
|
||||
} else if (value === cipher.login.totp) {
|
||||
value = await this.totpService.getCode(value);
|
||||
}
|
||||
|
||||
if (!cipher.viewPassword) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.platformUtilsService.copyToClipboard(value, { window: window });
|
||||
this.platformUtilsService.showToast('info', null,
|
||||
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
|
||||
|
||||
if (typeI18nKey === 'password' || typeI18nKey === 'verificationCodeTotp') {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledHiddenFieldVisible, cipher.id);
|
||||
} else if (typeI18nKey === 'securityCode') {
|
||||
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
||||
}
|
||||
if (!cipher.viewPassword) {
|
||||
return;
|
||||
}
|
||||
|
||||
displayTotpCopyButton(cipher: CipherView) {
|
||||
return (cipher?.login?.hasTotp ?? false) &&
|
||||
(cipher.organizationUseTotp || this.userHasPremiumAccess);
|
||||
}
|
||||
this.platformUtilsService.copyToClipboard(value, { window: window });
|
||||
this.platformUtilsService.showToast(
|
||||
"info",
|
||||
null,
|
||||
this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey))
|
||||
);
|
||||
|
||||
view() {
|
||||
this.onView.emit(this.cipher);
|
||||
if (typeI18nKey === "password" || typeI18nKey === "verificationCodeTotp") {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledHiddenFieldVisible, cipher.id);
|
||||
} else if (typeI18nKey === "securityCode") {
|
||||
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
||||
}
|
||||
}
|
||||
|
||||
displayTotpCopyButton(cipher: CipherView) {
|
||||
return (
|
||||
(cipher?.login?.hasTotp ?? false) && (cipher.organizationUseTotp || this.userHasPremiumAccess)
|
||||
);
|
||||
}
|
||||
|
||||
view() {
|
||||
this.onView.emit(this.cipher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
<button type="button" (click)="selectCipher(cipher)" (dblclick)="launchCipher(cipher)" appStopClick
|
||||
title="{{title}} - {{cipher.name}}" class="box-content-row box-content-row-flex virtual-scroll-item">
|
||||
<div class="row-main">
|
||||
<app-vault-icon [cipher]="cipher"></app-vault-icon>
|
||||
<div class="row-main-content">
|
||||
<span class="text">
|
||||
{{cipher.name}}
|
||||
<ng-container *ngIf="cipher.organizationId">
|
||||
<i class="fa fa-cube text-muted" title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="cipher.hasAttachments">
|
||||
<i class="fa fa-paperclip text-muted" title="{{'attachments' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'attachments' | i18n}}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span class="detail">{{cipher.subTitle}}</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
(click)="selectCipher(cipher)"
|
||||
(dblclick)="launchCipher(cipher)"
|
||||
appStopClick
|
||||
title="{{ title }} - {{ cipher.name }}"
|
||||
class="box-content-row box-content-row-flex virtual-scroll-item"
|
||||
>
|
||||
<div class="row-main">
|
||||
<app-vault-icon [cipher]="cipher"></app-vault-icon>
|
||||
<div class="row-main-content">
|
||||
<span class="text">
|
||||
{{ cipher.name }}
|
||||
<ng-container *ngIf="cipher.organizationId">
|
||||
<i class="fa fa-cube text-muted" title="{{ 'shared' | i18n }}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="cipher.hasAttachments">
|
||||
<i
|
||||
class="fa fa-paperclip text-muted"
|
||||
title="{{ 'attachments' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "attachments" | i18n }}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span class="detail">{{ cipher.subTitle }}</span>
|
||||
</div>
|
||||
<app-action-buttons [cipher]="cipher" [showView]="showView" (onView)="viewCipher(cipher)" (launchEvent)="launchCipher(cipher)"
|
||||
class="action-buttons">
|
||||
</app-action-buttons>
|
||||
</div>
|
||||
<app-action-buttons
|
||||
[cipher]="cipher"
|
||||
[showView]="showView"
|
||||
(onView)="viewCipher(cipher)"
|
||||
(launchEvent)="launchCipher(cipher)"
|
||||
class="action-buttons"
|
||||
>
|
||||
</app-action-buttons>
|
||||
</button>
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
@Component({
|
||||
selector: 'app-cipher-row',
|
||||
templateUrl: 'cipher-row.component.html',
|
||||
selector: "app-cipher-row",
|
||||
templateUrl: "cipher-row.component.html",
|
||||
})
|
||||
export class CipherRowComponent {
|
||||
@Output() onSelected = new EventEmitter<CipherView>();
|
||||
@Output() launchEvent = new EventEmitter<CipherView>();
|
||||
@Output() onView = new EventEmitter<CipherView>();
|
||||
@Input() cipher: CipherView;
|
||||
@Input() showView = false;
|
||||
@Input() title: string;
|
||||
@Output() onSelected = new EventEmitter<CipherView>();
|
||||
@Output() launchEvent = new EventEmitter<CipherView>();
|
||||
@Output() onView = new EventEmitter<CipherView>();
|
||||
@Input() cipher: CipherView;
|
||||
@Input() showView = false;
|
||||
@Input() title: string;
|
||||
|
||||
selectCipher(c: CipherView) {
|
||||
this.onSelected.emit(c);
|
||||
}
|
||||
selectCipher(c: CipherView) {
|
||||
this.onSelected.emit(c);
|
||||
}
|
||||
|
||||
launchCipher(c: CipherView) {
|
||||
this.launchEvent.emit(c);
|
||||
}
|
||||
launchCipher(c: CipherView) {
|
||||
this.launchEvent.emit(c);
|
||||
}
|
||||
|
||||
viewCipher(c: CipherView) {
|
||||
this.onView.emit(c);
|
||||
}
|
||||
viewCipher(c: CipherView) {
|
||||
this.onView.emit(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,56 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<h1 class="box-header">{{'passwordConfirmation' | i18n}}</h1>
|
||||
<div class="box-content">
|
||||
<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>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'passwordConfirmationDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{'ok' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{'cancel' | i18n}}
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<h1 class="box-header">{{ "passwordConfirmation" | i18n }}</h1>
|
||||
<div class="box-content">
|
||||
<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
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "passwordConfirmationDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{ "ok" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { PasswordRepromptComponent as BasePasswordRepromptComponent } from 'jslib-angular/components/password-reprompt.component';
|
||||
import { PasswordRepromptComponent as BasePasswordRepromptComponent } from "jslib-angular/components/password-reprompt.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: 'password-reprompt.component.html',
|
||||
templateUrl: "password-reprompt.component.html",
|
||||
})
|
||||
export class PasswordRepromptComponent extends BasePasswordRepromptComponent {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ng-container>
|
||||
<button type="button" (click)="expand()" appA11yTitle="{{'popOutNewWindow' | i18n}}">
|
||||
<i class="fa fa-external-link fa-rotate-270 fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" (click)="expand()" appA11yTitle="{{ 'popOutNewWindow' | i18n }}">
|
||||
<i class="fa fa-external-link fa-rotate-270 fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-pop-out',
|
||||
templateUrl: 'pop-out.component.html',
|
||||
selector: "app-pop-out",
|
||||
templateUrl: "pop-out.component.html",
|
||||
})
|
||||
export class PopOutComponent implements OnInit {
|
||||
@Input() show = true;
|
||||
@Input() show = true;
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService,
|
||||
private popupUtilsService: PopupUtilsService) { }
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private popupUtilsService: PopupUtilsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.show) {
|
||||
if (this.popupUtilsService.inSidebar(window) && this.platformUtilsService.isFirefox()) {
|
||||
this.show = false;
|
||||
}
|
||||
}
|
||||
ngOnInit() {
|
||||
if (this.show) {
|
||||
if (this.popupUtilsService.inSidebar(window) && this.platformUtilsService.isFirefox()) {
|
||||
this.show = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expand() {
|
||||
this.popupUtilsService.popOut(window);
|
||||
}
|
||||
expand() {
|
||||
this.popupUtilsService.popOut(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,90 @@
|
||||
<button type="button" *ngFor="let s of sends" (click)="selectSend(s)" appStopClick title="{{title}} - {{s.name}}"
|
||||
class="box-content-row box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<div class="app-vault-icon">
|
||||
<div class="icon" aria-hidden="true">
|
||||
<i class="fa fa-fw fa-lg fa-file-text-o" *ngIf="s.type === sendType.Text"></i>
|
||||
<i class="fa fa-fw fa-lg fa-file-o" *ngIf="s.type === sendType.File"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-main-content">
|
||||
<span class="text">
|
||||
{{s.name}}
|
||||
<ng-container *ngIf="s.disabled">
|
||||
<i class="fa fa-warning text-muted" title="{{'disabled' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'disabled' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.password">
|
||||
<i class="fa fa-key text-muted" title="{{'passwordProtected' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'passwordProtected' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.maxAccessCountReached">
|
||||
<i class="fa fa-ban text-muted" title="{{'maxAccessCountReached' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'maxAccessCountReached' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.expired">
|
||||
<i class="fa fa-clock-o text-muted" title="{{'expired' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'expired' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i class="fa fa-trash text-muted" title="{{'pendingDeletion' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'pendingDeletion' | i18n}}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span class="detail">{{s.deletionDate | date:'medium'}}</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
*ngFor="let s of sends"
|
||||
(click)="selectSend(s)"
|
||||
appStopClick
|
||||
title="{{ title }} - {{ s.name }}"
|
||||
class="box-content-row box-content-row-flex"
|
||||
>
|
||||
<div class="row-main">
|
||||
<div class="app-vault-icon">
|
||||
<div class="icon" aria-hidden="true">
|
||||
<i class="fa fa-fw fa-lg fa-file-text-o" *ngIf="s.type === sendType.Text"></i>
|
||||
<i class="fa fa-fw fa-lg fa-file-o" *ngIf="s.type === sendType.File"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'copySendLink' | i18n}}"
|
||||
(click)="copySendLink(s)">
|
||||
<i class="fa fa-lg fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="row-btn" [ngClass]="{'disabled' : disabledByPolicy}" appStopClick appStopProp
|
||||
appA11yTitle="{{'removePassword' | i18n}}" (click)="removePassword(s)" *ngIf="s.password">
|
||||
<i class="fa fa-lg fa-undo" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'delete' | i18n}}" (click)="delete(s)">
|
||||
<i class="fa fa-lg fa-trash-o" aria-hidden="true"></i>
|
||||
</span>
|
||||
<div class="row-main-content">
|
||||
<span class="text">
|
||||
{{ s.name }}
|
||||
<ng-container *ngIf="s.disabled">
|
||||
<i
|
||||
class="fa fa-warning text-muted"
|
||||
title="{{ 'disabled' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "disabled" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.password">
|
||||
<i
|
||||
class="fa fa-key text-muted"
|
||||
title="{{ 'passwordProtected' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "passwordProtected" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.maxAccessCountReached">
|
||||
<i
|
||||
class="fa fa-ban text-muted"
|
||||
title="{{ 'maxAccessCountReached' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "maxAccessCountReached" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.expired">
|
||||
<i class="fa fa-clock-o text-muted" title="{{ 'expired' | i18n }}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "expired" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i
|
||||
class="fa fa-trash text-muted"
|
||||
title="{{ 'pendingDeletion' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span class="detail">{{ s.deletionDate | date: "medium" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'copySendLink' | i18n }}"
|
||||
(click)="copySendLink(s)"
|
||||
>
|
||||
<i class="fa fa-lg fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
[ngClass]="{ disabled: disabledByPolicy }"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'removePassword' | i18n }}"
|
||||
(click)="removePassword(s)"
|
||||
*ngIf="s.password"
|
||||
>
|
||||
<i class="fa fa-lg fa-undo" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appStopProp
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
(click)="delete(s)"
|
||||
>
|
||||
<i class="fa fa-lg fa-trash-o" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,42 +1,37 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { SendView } from 'jslib-common/models/view/sendView';
|
||||
import { SendView } from "jslib-common/models/view/sendView";
|
||||
|
||||
import { SendType } from 'jslib-common/enums/sendType';
|
||||
import { SendType } from "jslib-common/enums/sendType";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-list',
|
||||
templateUrl: 'send-list.component.html',
|
||||
selector: "app-send-list",
|
||||
templateUrl: "send-list.component.html",
|
||||
})
|
||||
export class SendListComponent {
|
||||
@Input() sends: SendView[];
|
||||
@Input() title: string;
|
||||
@Input() disabledByPolicy = false;
|
||||
@Output() onSelected = new EventEmitter<SendView>();
|
||||
@Output() onCopySendLink = new EventEmitter<SendView>();
|
||||
@Output() onRemovePassword = new EventEmitter<SendView>();
|
||||
@Output() onDeleteSend = new EventEmitter<SendView>();
|
||||
@Input() sends: SendView[];
|
||||
@Input() title: string;
|
||||
@Input() disabledByPolicy = false;
|
||||
@Output() onSelected = new EventEmitter<SendView>();
|
||||
@Output() onCopySendLink = new EventEmitter<SendView>();
|
||||
@Output() onRemovePassword = new EventEmitter<SendView>();
|
||||
@Output() onDeleteSend = new EventEmitter<SendView>();
|
||||
|
||||
sendType = SendType;
|
||||
sendType = SendType;
|
||||
|
||||
selectSend(s: SendView) {
|
||||
this.onSelected.emit(s);
|
||||
}
|
||||
selectSend(s: SendView) {
|
||||
this.onSelected.emit(s);
|
||||
}
|
||||
|
||||
copySendLink(s: SendView) {
|
||||
this.onCopySendLink.emit(s);
|
||||
}
|
||||
copySendLink(s: SendView) {
|
||||
this.onCopySendLink.emit(s);
|
||||
}
|
||||
|
||||
removePassword(s: SendView) {
|
||||
this.onRemovePassword.emit(s);
|
||||
}
|
||||
removePassword(s: SendView) {
|
||||
this.onRemovePassword.emit(s);
|
||||
}
|
||||
|
||||
delete(s: SendView) {
|
||||
this.onDeleteSend.emit(s);
|
||||
}
|
||||
delete(s: SendView) {
|
||||
this.onDeleteSend.emit(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,65 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{'setYourPinCode' | i18n}}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="pin">{{'pin' | i18n}}</label>
|
||||
<input id="pin" type="{{showPin ? 'text' : 'password'}}" name="Pin"
|
||||
class="monospaced" [(ngModel)]="pin" required appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
(click)="toggleVisibility()" [attr.aria-pressed]="showPin">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPin, 'fa-eye-slash': showPin}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
||||
<label for="masterPasswordOnRestart">
|
||||
<input type="checkbox" id="masterPasswordOnRestart" name="MasterPasswordOnRestart"
|
||||
[(ngModel)]="masterPassOnRestart">
|
||||
<span>{{'lockWithMasterPassOnRestart' | i18n}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{'ok' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{'cancel' | i18n}}
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ "setYourPinCode" | i18n }}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="pin">{{ "pin" | i18n }}</label>
|
||||
<input
|
||||
id="pin"
|
||||
type="{{ showPin ? 'text' : 'password' }}"
|
||||
name="Pin"
|
||||
class="monospaced"
|
||||
[(ngModel)]="pin"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleVisibility()"
|
||||
[attr.aria-pressed]="showPin"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPin, 'fa-eye-slash': showPin }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
||||
<label for="masterPasswordOnRestart">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="masterPasswordOnRestart"
|
||||
name="MasterPasswordOnRestart"
|
||||
[(ngModel)]="masterPassOnRestart"
|
||||
/>
|
||||
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{ "ok" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { SetPinComponent as BaseSetPinComponent } from 'jslib-angular/components/set-pin.component';
|
||||
import { SetPinComponent as BaseSetPinComponent } from "jslib-angular/components/set-pin.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: 'set-pin.component.html',
|
||||
templateUrl: "set-pin.component.html",
|
||||
})
|
||||
export class SetPinComponent extends BaseSetPinComponent {}
|
||||
|
||||
@@ -1,25 +1,46 @@
|
||||
<ng-container *ngIf="!usesKeyConnector">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control"
|
||||
[formControl]="secret" required appAutofocus appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="password"
|
||||
name="MasterPasswordHash"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="usesKeyConnector">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label class="d-block">{{'sendVerificationCode' | i18n}}</label>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="requestOTP()" [disabled]="disableRequestOTP">
|
||||
{{'sendCode' | i18n}}
|
||||
</button>
|
||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
|
||||
{{'codeSent' | i18n}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="requestOTP()"
|
||||
[disabled]="disableRequestOTP"
|
||||
>
|
||||
{{ "sendCode" | i18n }}
|
||||
</button>
|
||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
|
||||
{{ "codeSent" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="verificationCode">{{'verificationCode' | i18n}}</label>
|
||||
<input id="verificationCode" type="input" name="verificationCode" class="form-control"
|
||||
[formControl]="secret" required appAutofocus appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="verificationCode"
|
||||
type="input"
|
||||
name="verificationCode"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
import {
|
||||
animate,
|
||||
style,
|
||||
transition,
|
||||
trigger,
|
||||
} from '@angular/animations';
|
||||
import { Component } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component } from "@angular/core";
|
||||
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import { VerifyMasterPasswordComponent as BaseComponent } from 'jslib-angular/components/verify-master-password.component';
|
||||
import { VerifyMasterPasswordComponent as BaseComponent } from "jslib-angular/components/verify-master-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-verify-master-password',
|
||||
templateUrl: 'verify-master-password.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VerifyMasterPasswordComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger('sent', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate('100ms', style({ opacity: 1 })),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
selector: "app-verify-master-password",
|
||||
templateUrl: "verify-master-password.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VerifyMasterPasswordComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger("sent", [
|
||||
transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class VerifyMasterPasswordComponent extends BaseComponent { }
|
||||
export class VerifyMasterPasswordComponent extends BaseComponent {}
|
||||
|
||||
@@ -1,40 +1,48 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<button type="button" appBlurClick type="button" (click)="close()">
|
||||
<span class="header-icon" aria-hidden="true"><i class="fa fa-chevron-left"></i></span>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'passwordHistory' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="button" appBlurClick type="button" (click)="clear()">
|
||||
{{'clear' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="left">
|
||||
<button type="button" appBlurClick type="button" (click)="close()">
|
||||
<span class="header-icon" aria-hidden="true"><i class="fa fa-chevron-left"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "passwordHistory" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="button" appBlurClick type="button" (click)="clear()">
|
||||
{{ "clear" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box list full-list" *ngIf="history && history.length">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<div class="row-main-content">
|
||||
<div class="monospaced password-wrapper" appSelectCopy
|
||||
[innerHTML]="h.password | colorPassword"></div>
|
||||
<span class="detail">{{h.date | date:'medium'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
||||
(click)="copy(h.password)">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list full-list" *ngIf="history && history.length">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<div class="row-main-content">
|
||||
<div
|
||||
class="monospaced password-wrapper"
|
||||
appSelectCopy
|
||||
[innerHTML]="h.password | colorPassword"
|
||||
></div>
|
||||
<span class="detail">{{ h.date | date: "medium" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(h.password)"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!history || !history.length">
|
||||
<p>{{'noPasswordsInList' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!history || !history.length">
|
||||
<p>{{ "noPasswordsInList" | i18n }}</p>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent,
|
||||
} from 'jslib-angular/components/password-generator-history.component';
|
||||
import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "jslib-angular/components/password-generator-history.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-generator-history',
|
||||
templateUrl: 'password-generator-history.component.html',
|
||||
selector: "app-password-generator-history",
|
||||
templateUrl: "password-generator-history.component.html",
|
||||
})
|
||||
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
|
||||
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService, private location: Location) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
constructor(
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
private location: Location
|
||||
) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.location.back();
|
||||
}
|
||||
close() {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,128 +1,229 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<app-pop-out [show]="!showSelect"></app-pop-out>
|
||||
<button type="button" appBlurClick (click)="close()" *ngIf="showSelect">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'passGen' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="button" appBlurClick (click)="select()" *ngIf="showSelect">{{'select' | i18n}}</button>
|
||||
</div>
|
||||
<div class="left">
|
||||
<app-pop-out [show]="!showSelect"></app-pop-out>
|
||||
<button type="button" appBlurClick (click)="close()" *ngIf="showSelect">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "passGen" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="button" appBlurClick (click)="select()" *ngIf="showSelect">
|
||||
{{ "select" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
||||
{{'passwordGeneratorPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<div class="password-block">
|
||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="password-block">
|
||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<div class="box-content single-line">
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row text-primary"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="regenerate()"
|
||||
>
|
||||
{{ "regeneratePassword" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row text-primary"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="copy()"
|
||||
>
|
||||
{{ "copyPassword" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<div class="box-content single-line">
|
||||
<button type="button" class="box-content-row text-primary" appStopClick appBlurClick
|
||||
(click)="regenerate()">{{'regeneratePassword' | i18n}}</button>
|
||||
<button type="button" class="box-content-row text-primary" appStopClick appBlurClick
|
||||
(click)="copy()">{{'copyPassword' | i18n}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row box-content-row-flex" routerLink="/generator-history">
|
||||
<div class="row-main">{{ "passwordHistory" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row box-content-row-flex" routerLink="/generator-history">
|
||||
<div class="row-main">{{'passwordHistory' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{ "options" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<label class="sr-only radio-header">{{ "type" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of passTypeOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="options.type"
|
||||
name="Type_{{ o.value }}"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveOptions()"
|
||||
[checked]="options.type === o.value"
|
||||
/>
|
||||
<label for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="options.type === 'passphrase'">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||
<input
|
||||
id="num-words"
|
||||
type="number"
|
||||
min="3"
|
||||
max="20"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.numWords"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||
<input
|
||||
id="word-separator"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
(input)="saveOptions()"
|
||||
[(ngModel)]="options.wordSeparator"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||
<input
|
||||
id="capitalize"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.capitalize"
|
||||
[disabled]="enforcedPolicyOptions?.capitalize"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||
<input
|
||||
id="include-number"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.includeNumber"
|
||||
[disabled]="enforcedPolicyOptions?.includeNumber"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="options.type === 'password'">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||
<label for="length">{{ "length" | i18n }}</label>
|
||||
<input
|
||||
id="length"
|
||||
type="number"
|
||||
min="5"
|
||||
max="128"
|
||||
[(ngModel)]="options.length"
|
||||
(change)="saveOptions()"
|
||||
/>
|
||||
<input
|
||||
id="lengthRange"
|
||||
type="range"
|
||||
min="5"
|
||||
max="128"
|
||||
step="1"
|
||||
[(ngModel)]="options.length"
|
||||
(change)="sliderChanged()"
|
||||
(input)="sliderInput()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="uppercase">A-Z</label>
|
||||
<input
|
||||
id="uppercase"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||
[disabled]="enforcedPolicyOptions.useUppercase"
|
||||
[(ngModel)]="options.uppercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<input
|
||||
id="lowercase"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||
[disabled]="enforcedPolicyOptions.useLowercase"
|
||||
[(ngModel)]="options.lowercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input
|
||||
id="numbers"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||
[disabled]="enforcedPolicyOptions.useNumbers"
|
||||
[(ngModel)]="options.number"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input
|
||||
id="special"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||
[disabled]="enforcedPolicyOptions.useSpecial"
|
||||
[(ngModel)]="options.special"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{'options' | i18n}}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<label class="sr-only radio-header">{{'type' | i18n}}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of passTypeOptions">
|
||||
<input type="radio" [(ngModel)]="options.type" name="Type_{{o.value}}" id="type_{{o.value}}"
|
||||
[value]="o.value" (change)="saveOptions()" [checked]="options.type === o.value">
|
||||
<label for="type_{{o.value}}">
|
||||
{{o.name}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||
<input
|
||||
id="min-number"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.minNumber"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||
<input
|
||||
id="min-special"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.minSpecial"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="ambiguous">{{ "avoidAmbChar" | i18n }}</label>
|
||||
<input
|
||||
id="ambiguous"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="avoidAmbiguous"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="options.type === 'passphrase'">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="num-words">{{'numWords' | i18n}}</label>
|
||||
<input id="num-words" type="number" min="3" max="20" (change)="saveOptions()"
|
||||
[(ngModel)]="options.numWords">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="word-separator">{{'wordSeparator' | i18n}}</label>
|
||||
<input id="word-separator" type="text" maxlength="1" (input)="saveOptions()"
|
||||
[(ngModel)]="options.wordSeparator">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="capitalize">{{'capitalize' | i18n}}</label>
|
||||
<input id="capitalize" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.capitalize"
|
||||
[disabled]="enforcedPolicyOptions?.capitalize">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="include-number">{{'includeNumber' | i18n}}</label>
|
||||
<input id="include-number" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.includeNumber"
|
||||
[disabled]="enforcedPolicyOptions?.includeNumber">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="options.type === 'password'">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||
<label for="length">{{'length' | i18n}}</label>
|
||||
<input id="length" type="number" min="5" max="128" [(ngModel)]="options.length"
|
||||
(change)="saveOptions()">
|
||||
<input id="lengthRange" type="range" min="5" max="128" step="1" [(ngModel)]="options.length"
|
||||
(change)="sliderChanged()" (input)="sliderInput()">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="uppercase">A-Z</label>
|
||||
<input id="uppercase" type="checkbox" (change)="saveOptions()" attr.aria-label="{{'uppercase' | i18n}}"
|
||||
[disabled]="enforcedPolicyOptions.useUppercase" [(ngModel)]="options.uppercase">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<input id="lowercase" type="checkbox" (change)="saveOptions()" attr.aria-label="{{'lowercase' | i18n}}"
|
||||
[disabled]="enforcedPolicyOptions.useLowercase" [(ngModel)]="options.lowercase">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input id="numbers" type="checkbox" (change)="saveOptions()" attr.aria-label="{{'numbers' | i18n}}"
|
||||
[disabled]="enforcedPolicyOptions.useNumbers" [(ngModel)]="options.number">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input id="special" type="checkbox" (change)="saveOptions()" attr.aria-label="{{'specialCharacters' | i18n}}"
|
||||
[disabled]="enforcedPolicyOptions.useSpecial" [(ngModel)]="options.special">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-number">{{'minNumbers' | i18n}}</label>
|
||||
<input id="min-number" type="number" min="0" max="9" (change)="saveOptions()"
|
||||
[(ngModel)]="options.minNumber">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-special">{{'minSpecial' | i18n}}</label>
|
||||
<input id="min-special" type="number" min="0" max="9" (change)="saveOptions()"
|
||||
[(ngModel)]="options.minSpecial">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="ambiguous">{{'avoidAmbChar' | i18n}}</label>
|
||||
<input id="ambiguous" type="checkbox" (change)="saveOptions()" [(ngModel)]="avoidAmbiguous">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</content>
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
import {
|
||||
PasswordGeneratorComponent as BasePasswordGeneratorComponent,
|
||||
} from 'jslib-angular/components/password-generator.component';
|
||||
import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-generator',
|
||||
templateUrl: 'password-generator.component.html',
|
||||
selector: "app-password-generator",
|
||||
templateUrl: "password-generator.component.html",
|
||||
})
|
||||
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
|
||||
private cipherState: CipherView;
|
||||
private cipherState: CipherView;
|
||||
|
||||
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService, private stateService: StateService,
|
||||
private location: Location) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
constructor(
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
private stateService: StateService,
|
||||
private location: Location
|
||||
) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const addEditCipherInfo = await this.stateService.get<any>('addEditCipherInfo');
|
||||
if (addEditCipherInfo != null) {
|
||||
this.cipherState = addEditCipherInfo.cipher;
|
||||
}
|
||||
this.showSelect = this.cipherState != null;
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const addEditCipherInfo = await this.stateService.get<any>("addEditCipherInfo");
|
||||
if (addEditCipherInfo != null) {
|
||||
this.cipherState = addEditCipherInfo.cipher;
|
||||
}
|
||||
this.showSelect = this.cipherState != null;
|
||||
}
|
||||
|
||||
select() {
|
||||
super.select();
|
||||
this.cipherState.login.password = this.password;
|
||||
this.close();
|
||||
}
|
||||
select() {
|
||||
super.select();
|
||||
this.cipherState.login.password = this.password;
|
||||
this.close();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.location.back();
|
||||
}
|
||||
close() {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="__BROWSER__">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Bitwarden</title>
|
||||
<base href="">
|
||||
</head>
|
||||
<body>
|
||||
<base href="" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root>
|
||||
<div id="loading"><i class="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i></div>
|
||||
<div id="loading"><i class="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i></div>
|
||||
</app-root>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
// tslint:disable-next-line
|
||||
require('./scss/popup.scss');
|
||||
require("./scss/popup.scss");
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
import { AppModule } from "./app.module";
|
||||
|
||||
if (process.env.ENV === 'production') {
|
||||
enableProdMode();
|
||||
if (process.env.ENV === "production") {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
function init() {
|
||||
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
||||
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* tslint:disable */
|
||||
import 'core-js/stable';
|
||||
import 'date-input-polyfill';
|
||||
import 'web-animations-js';
|
||||
import 'zone.js/dist/zone';
|
||||
import "core-js/stable";
|
||||
import "date-input-polyfill";
|
||||
import "web-animations-js";
|
||||
import "zone.js/dist/zone";
|
||||
/* tslint:enable */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="content">
|
||||
<p class="text-center">{{privateModeMessage}}</p>
|
||||
<button type="button" class="btn primary block" (click)="learnMore()">
|
||||
<b>{{learnMoreMessage}}</b>
|
||||
</button>
|
||||
<p class="text-center">{{ privateModeMessage }}</p>
|
||||
<button type="button" class="btn primary block" (click)="learnMore()">
|
||||
<b>{{ learnMoreMessage }}</b>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { BrowserApi } from '../browser/browserApi';
|
||||
import { BrowserApi } from "../browser/browserApi";
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: 'app-private-mode',
|
||||
templateUrl: 'private-mode.component.html',
|
||||
selector: "app-private-mode",
|
||||
templateUrl: "private-mode.component.html",
|
||||
})
|
||||
export class PrivateModeComponent implements OnInit {
|
||||
privateModeMessage: string;
|
||||
learnMoreMessage: string;
|
||||
privateModeMessage: string;
|
||||
learnMoreMessage: string;
|
||||
|
||||
ngOnInit() {
|
||||
this.privateModeMessage = chrome.i18n.getMessage('privateModeMessage');
|
||||
this.learnMoreMessage = chrome.i18n.getMessage('learnMore');
|
||||
}
|
||||
ngOnInit() {
|
||||
this.privateModeMessage = chrome.i18n.getMessage("privateModeMessage");
|
||||
this.learnMoreMessage = chrome.i18n.getMessage("learnMore");
|
||||
}
|
||||
|
||||
learnMore() {
|
||||
BrowserApi.createNewTab('https://help.bitwarden.com/article/extension-wont-load-in-private-mode/');
|
||||
}
|
||||
learnMore() {
|
||||
BrowserApi.createNewTab(
|
||||
"https://help.bitwarden.com/article/extension-wont-load-in-private-mode/"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,438 +1,461 @@
|
||||
@import "variables.scss";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-family: $font-family-sans-serif;
|
||||
font-size: $font-size-base;
|
||||
line-height: $line-height-base;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
html,
|
||||
body {
|
||||
font-family: $font-family-sans-serif;
|
||||
font-size: $font-size-base;
|
||||
line-height: $line-height-base;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 375px !important;
|
||||
height: 600px !important;
|
||||
overflow: hidden;
|
||||
color: $text-color;
|
||||
background-color: $background-color;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
background-color: themed("backgroundColor");
|
||||
}
|
||||
|
||||
&.body-sm {
|
||||
width: 375px !important;
|
||||
height: 600px !important;
|
||||
overflow: hidden;
|
||||
color: $text-color;
|
||||
background-color: $background-color;
|
||||
height: 500px !important;
|
||||
}
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('textColor');
|
||||
background-color: themed('backgroundColor');
|
||||
}
|
||||
&.body-xs {
|
||||
width: 375px !important;
|
||||
height: 300px !important;
|
||||
}
|
||||
|
||||
&.body-sm {
|
||||
width: 375px !important;
|
||||
height: 500px !important;
|
||||
}
|
||||
|
||||
&.body-xs {
|
||||
width: 375px !important;
|
||||
height: 300px !important;
|
||||
}
|
||||
|
||||
&.body-full {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
&.body-full {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: $font-family-sans-serif;
|
||||
font-size: $font-size-base;
|
||||
font-weight: normal;
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: $font-family-sans-serif;
|
||||
font-size: $font-size-base;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-bottom: 10px;
|
||||
ul,
|
||||
ol {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
text-decoration: none;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@include themify($themes) {
|
||||
color: themed('primaryColor');
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
@include themify($themes) {
|
||||
color: darken(themed('primaryColor'), 6%);
|
||||
}
|
||||
color: darken(themed("primaryColor"), 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
@include themify($themes) {
|
||||
color: themed('textColor');
|
||||
background-color: themed('inputBackgroundColor');
|
||||
}
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
background-color: themed("inputBackgroundColor");
|
||||
}
|
||||
}
|
||||
|
||||
input, select, textarea, button {
|
||||
font-size: $font-size-base;
|
||||
font-family: $font-family-sans-serif;
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
button {
|
||||
font-size: $font-size-base;
|
||||
font-family: $font-family-sans-serif;
|
||||
}
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
content::-webkit-scrollbar, cdk-virtual-scroll-viewport::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
content::-webkit-scrollbar,
|
||||
cdk-virtual-scroll-viewport::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
content::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
cdk-virtual-scroll-viewport::-webkit-scrollbar-track {
|
||||
@include themify($themes) {
|
||||
background-color: themed('backgroundColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("backgroundColor");
|
||||
}
|
||||
}
|
||||
|
||||
content::-webkit-scrollbar-thumb, cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
margin-right: 1px;
|
||||
content::-webkit-scrollbar-thumb,
|
||||
cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
margin-right: 1px;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed("scrollbarColor");
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include themify($themes) {
|
||||
background-color: themed('scrollbarColor');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include themify($themes) {
|
||||
background-color: themed('scrollbarHoverColor');
|
||||
}
|
||||
background-color: themed("scrollbarHoverColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
min-height: 44px;
|
||||
max-height: 44px;
|
||||
min-height: 44px;
|
||||
max-height: 44px;
|
||||
display: flex;
|
||||
border-bottom: 1px solid #000000;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("headerColor");
|
||||
background-color: themed("headerBackgroundColor");
|
||||
border-bottom-color: themed("headerBorderColor");
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
border-bottom: 1px solid #000000;
|
||||
min-width: -webkit-min-content; /* Workaround to Chrome bug */
|
||||
.header-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
app-pop-out > button,
|
||||
div > button,
|
||||
div > a {
|
||||
border: none;
|
||||
padding: 0 10px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('headerColor');
|
||||
background-color: themed('headerBackgroundColor');
|
||||
border-bottom-color: themed('headerBorderColor');
|
||||
color: themed("headerColor");
|
||||
background-color: themed("headerBackgroundColor");
|
||||
}
|
||||
|
||||
.left, .right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-width: -webkit-min-content; /* Workaround to Chrome bug */
|
||||
.header-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
&:hover,
|
||||
&:focus {
|
||||
@include themify($themes) {
|
||||
background-color: themed("headerBackgroundHoverColor");
|
||||
color: themed("headerColor");
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
justify-content: flex-end;
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
min-width: 0;
|
||||
&[disabled] {
|
||||
opacity: 0.65;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
app-pop-out > button, div > button, div > a {
|
||||
border: none;
|
||||
padding: 0 10px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
i + span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
app-pop-out {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding: 7px 10px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 20px;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("headerInputPlaceholderColor");
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 5px 10px 5px 30px;
|
||||
border-radius: $border-radius;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed("headerInputBackgroundColor");
|
||||
color: themed("headerInputColor");
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-radius: $border-radius;
|
||||
outline: none;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('headerColor');
|
||||
background-color: themed('headerBackgroundColor');
|
||||
background-color: themed("headerInputBackgroundFocusColor");
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
@include themify($themes) {
|
||||
background-color: themed('headerBackgroundHoverColor');
|
||||
color: themed('headerColor');
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.65;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
i + span {
|
||||
margin-left: 5px;
|
||||
&::-webkit-input-placeholder {
|
||||
@include themify($themes) {
|
||||
color: themed("headerInputPlaceholderColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-pop-out {
|
||||
display: flex;
|
||||
.left + .search {
|
||||
padding-left: 0;
|
||||
|
||||
.fa {
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding: 7px 10px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 20px;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('headerInputPlaceholderColor');
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 5px 10px 5px 30px;
|
||||
border-radius: $border-radius;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('headerInputBackgroundColor');
|
||||
color: themed('headerInputColor');
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-radius: $border-radius;
|
||||
outline: none;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('headerInputBackgroundFocusColor');
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
@include themify($themes) {
|
||||
color: themed('headerInputPlaceholderColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left + .search {
|
||||
padding-left: 0;
|
||||
|
||||
.fa {
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.search + .right {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.search + .right {
|
||||
margin-left: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
width: 100%;
|
||||
height: 55px;
|
||||
border-top: 1px solid #000000;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 55px;
|
||||
border-top: 1px solid #000000;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('tabBackgroundColor');
|
||||
border-top-color: themed('borderColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("tabBackgroundColor");
|
||||
border-top-color: themed("borderColor");
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
li {
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
a {
|
||||
text-align: center;
|
||||
display: block;
|
||||
padding: 7px 0;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
a {
|
||||
text-align: center;
|
||||
display: block;
|
||||
padding: 7px 0;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('mutedColor');
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
@include themify($themes) {
|
||||
background-color: themed('tabBackgroundHoverColor');
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
a {
|
||||
@include themify($themes) {
|
||||
color: themed('primaryColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("mutedColor");
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@include themify($themes) {
|
||||
background-color: themed("tabBackgroundHoverColor");
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
a {
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-root {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 980;
|
||||
@include themify($themes) {
|
||||
background-color: themed('backgroundColor');
|
||||
}
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 980;
|
||||
@include themify($themes) {
|
||||
background-color: themed("backgroundColor");
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 601px) {
|
||||
app-login header {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
app-login header {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
|
||||
app-login content {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
app-login content {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
|
||||
app-two-factor header {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
app-two-factor header {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
|
||||
app-two-factor content {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
app-two-factor content {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
|
||||
app-lock header {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
app-lock header {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
|
||||
app-lock content {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
app-lock content {
|
||||
padding: 0 calc((100% - 500px) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
content {
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('backgroundColor');
|
||||
}
|
||||
|
||||
&.no-header {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.flex {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: calc(100% - 44px);
|
||||
|
||||
&.tab-page {
|
||||
height: calc(100% - 99px);
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("backgroundColor");
|
||||
}
|
||||
|
||||
&.no-header {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.flex {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: calc(100% - 44px);
|
||||
|
||||
&.tab-page {
|
||||
height: calc(100% - 99px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-page {
|
||||
content {
|
||||
bottom: 55px;
|
||||
}
|
||||
content {
|
||||
bottom: 55px;
|
||||
}
|
||||
}
|
||||
|
||||
.center-content, .no-items, .full-loading-spinner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
.center-content,
|
||||
.no-items,
|
||||
.full-loading-spinner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.no-items, .full-loading-spinner {
|
||||
text-align: center;
|
||||
.no-items,
|
||||
.full-loading-spinner {
|
||||
text-align: center;
|
||||
|
||||
.fa {
|
||||
margin-bottom: 10px;
|
||||
.fa {
|
||||
margin-bottom: 10px;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('disabledIconColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("disabledIconColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cdk-virtual-scroll
|
||||
.cdk-virtual-scroll-viewport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.cdk-virtual-scroll-content-wrapper {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,101 +1,101 @@
|
||||
@import "variables.scss";
|
||||
|
||||
.btn {
|
||||
border-radius: $border-radius;
|
||||
padding: 7px 15px;
|
||||
border: 1px solid #000000;
|
||||
font-size: $font-size-base;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
border-radius: $border-radius;
|
||||
padding: 7px 15px;
|
||||
border: 1px solid #000000;
|
||||
font-size: $font-size-base;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed("buttonBackgroundColor");
|
||||
border-color: themed("buttonBorderColor");
|
||||
color: themed("buttonColor");
|
||||
}
|
||||
|
||||
&.primary {
|
||||
@include themify($themes) {
|
||||
color: themed("buttonPrimaryColor");
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
@include themify($themes) {
|
||||
color: themed("buttonDangerColor");
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:not([disabled]) {
|
||||
cursor: pointer;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('buttonBackgroundColor');
|
||||
border-color: themed('buttonBorderColor');
|
||||
color: themed('buttonColor');
|
||||
background-color: darken(themed("buttonBackgroundColor"), 1.5%);
|
||||
border-color: darken(themed("buttonBorderColor"), 17%);
|
||||
color: darken(themed("buttonColor"), 10%);
|
||||
}
|
||||
|
||||
&.primary {
|
||||
@include themify($themes) {
|
||||
color: themed('buttonPrimaryColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: darken(themed("buttonPrimaryColor"), 6%);
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
@include themify($themes) {
|
||||
color: themed('buttonDangerColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: darken(themed("buttonDangerColor"), 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:not([disabled]) {
|
||||
cursor: pointer;
|
||||
&:focus:not([disabled]) {
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: darken(themed('buttonBackgroundColor'), 1.5%);
|
||||
border-color: darken(themed('buttonBorderColor'), 17%);
|
||||
color: darken(themed('buttonColor'), 10%);
|
||||
}
|
||||
|
||||
&.primary {
|
||||
@include themify($themes) {
|
||||
color: darken(themed('buttonPrimaryColor'), 6%);
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
@include themify($themes) {
|
||||
color: darken(themed('buttonDangerColor'), 6%);
|
||||
}
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: darken(themed("buttonBackgroundColor"), 6%);
|
||||
border-color: darken(themed("buttonBorderColor"), 25%);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus:not([disabled]) {
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
&[disabled] {
|
||||
opacity: 0.65;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: darken(themed('buttonBackgroundColor'), 6%);
|
||||
border-color: darken(themed('buttonBorderColor'), 25%);
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.65;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
&.block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.link,
|
||||
&.neutral {
|
||||
border: none !important;
|
||||
background: none !important;
|
||||
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
&.block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.link,
|
||||
&.neutral {
|
||||
border: none !important;
|
||||
background: none !important;
|
||||
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
.btn {
|
||||
&:focus {
|
||||
outline: auto;
|
||||
}
|
||||
.btn {
|
||||
&:focus {
|
||||
outline: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.box-content-row {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
@import "variables.scss";
|
||||
|
||||
html.browser_safari {
|
||||
body {
|
||||
height: 360px !important;
|
||||
body {
|
||||
height: 360px !important;
|
||||
|
||||
&.body-xs {
|
||||
height: 300px !important;
|
||||
}
|
||||
|
||||
&.body-full {
|
||||
height: 100% !important;
|
||||
}
|
||||
&.body-xs {
|
||||
height: 300px !important;
|
||||
}
|
||||
|
||||
header {
|
||||
.search .fa {
|
||||
left: 20px;
|
||||
}
|
||||
&.body-full {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.left + .search .fa {
|
||||
left: 10px;
|
||||
}
|
||||
header {
|
||||
.search .fa {
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
app-root {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #000000;
|
||||
.left + .search .fa {
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.theme_light app-root {
|
||||
border-color: #777777;
|
||||
}
|
||||
app-root {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
&.theme_nord app-root {
|
||||
border-color: #2e3440;
|
||||
}
|
||||
&.theme_light app-root {
|
||||
border-color: #777777;
|
||||
}
|
||||
|
||||
&.theme_nord app-root {
|
||||
border-color: #2e3440;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
.row {
|
||||
display: flex;
|
||||
margin: 0 -15px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin: 0 -15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
padding: 0 15px;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
@@ -1,375 +1,378 @@
|
||||
@import "variables.scss";
|
||||
|
||||
small, .small {
|
||||
font-size: $font-size-small;
|
||||
small,
|
||||
.small {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
@include themify($themes) {
|
||||
background-color: themed('primaryColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("primaryColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
@include themify($themes) {
|
||||
background-color: themed('successColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("successColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
@include themify($themes) {
|
||||
background-color: themed('dangerColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("dangerColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-info {
|
||||
@include themify($themes) {
|
||||
background-color: themed('infoColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("infoColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-warning {
|
||||
@include themify($themes) {
|
||||
background-color: themed('warningColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("warningColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
@include themify($themes) {
|
||||
color: themed('primaryColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-success {
|
||||
@include themify($themes) {
|
||||
color: themed('successColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("successColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
@include themify($themes) {
|
||||
color: themed('mutedColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("mutedColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-default {
|
||||
@include themify($themes) {
|
||||
color: themed('textColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("textColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
@include themify($themes) {
|
||||
color: themed('dangerColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("dangerColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-info {
|
||||
@include themify($themes) {
|
||||
color: themed('infoColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("infoColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
@include themify($themes) {
|
||||
color: themed('warningColor') !important;
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("warningColor") !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.font-weight-semibold {
|
||||
font-weight: 600;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
font-size: $font-size-large;
|
||||
margin-bottom: 20px;
|
||||
font-weight: normal;
|
||||
font-size: $font-size-large;
|
||||
margin-bottom: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.flex-right {
|
||||
margin-left: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.flex-bottom {
|
||||
margin-top: auto;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.no-vmargin {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.no-vpad {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.display-block {
|
||||
display: block !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.monospaced {
|
||||
font-family: $font-family-monospace;
|
||||
font-family: $font-family-monospace;
|
||||
}
|
||||
|
||||
.show-whitespace {
|
||||
white-space: pre-wrap;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.img-responsive {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.img-rounded {
|
||||
border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
border: 0 !important;
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.password-wrapper {
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
min-width: 0;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.password-number {
|
||||
@include themify($themes) {
|
||||
color: themed('passwordNumberColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("passwordNumberColor");
|
||||
}
|
||||
}
|
||||
|
||||
.password-special {
|
||||
@include themify($themes) {
|
||||
color: themed('passwordSpecialColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("passwordSpecialColor");
|
||||
}
|
||||
}
|
||||
|
||||
#duo-frame {
|
||||
background: url('../images/loading.svg') 0 0 no-repeat;
|
||||
width: 100%;
|
||||
height: 470px;
|
||||
margin-bottom: -10px;
|
||||
background: url("../images/loading.svg") 0 0 no-repeat;
|
||||
width: 100%;
|
||||
height: 470px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
#web-authn-frame {
|
||||
background: url('../images/loading.svg') 0 0 no-repeat;
|
||||
width: 100%;
|
||||
height: 310px;
|
||||
margin-bottom: -10px;
|
||||
background: url("../images/loading.svg") 0 0 no-repeat;
|
||||
width: 100%;
|
||||
height: 310px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
#hcaptcha_iframe {
|
||||
width: 100%;
|
||||
border: none;
|
||||
transition: height 0.25s linear;
|
||||
width: 100%;
|
||||
border: none;
|
||||
transition: height 0.25s linear;
|
||||
}
|
||||
|
||||
body.linux-webauthn {
|
||||
width: 485px !important;
|
||||
#web-authn-frame {
|
||||
iframe {
|
||||
width: 375px;
|
||||
margin: 0 55px;
|
||||
}
|
||||
width: 485px !important;
|
||||
#web-authn-frame {
|
||||
iframe {
|
||||
width: 375px;
|
||||
margin: 0 55px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-root > #loading {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: $text-muted;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: $text-muted;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('mutedColor');
|
||||
}
|
||||
@include themify($themes) {
|
||||
color: themed("mutedColor");
|
||||
}
|
||||
}
|
||||
|
||||
app-vault-icon {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
margin: 0 auto;
|
||||
width: 142px;
|
||||
height: 21px;
|
||||
background-size: 142px 21px;
|
||||
background-repeat: no-repeat;
|
||||
@include themify($themes) {
|
||||
background-image: url('../images/logo-' + themed('logoSuffix') + '@2x.png');
|
||||
}
|
||||
@media (min-width: 219px) {
|
||||
width: 189px;
|
||||
height: 28px;
|
||||
background-size: 189px 28px;
|
||||
}
|
||||
@media (min-width: 314px) {
|
||||
width: 284px;
|
||||
height: 43px;
|
||||
background-size: 284px 43px;
|
||||
}
|
||||
margin: 0 auto;
|
||||
width: 142px;
|
||||
height: 21px;
|
||||
background-size: 142px 21px;
|
||||
background-repeat: no-repeat;
|
||||
@include themify($themes) {
|
||||
background-image: url("../images/logo-" + themed("logoSuffix") + "@2x.png");
|
||||
}
|
||||
@media (min-width: 219px) {
|
||||
width: 189px;
|
||||
height: 28px;
|
||||
background-size: 189px 28px;
|
||||
}
|
||||
@media (min-width: 314px) {
|
||||
width: 284px;
|
||||
height: 43px;
|
||||
background-size: 284px 43px;
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
cursor: move;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.callout {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border: 1px solid #000000;
|
||||
border-left-width: 5px;
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border: 1px solid #000000;
|
||||
border-left-width: 5px;
|
||||
border-radius: 3px;
|
||||
@include themify($themes) {
|
||||
border-color: themed("calloutBorderColor");
|
||||
background-color: themed("calloutBackgroundColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3.callout-heading {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.callout-primary {
|
||||
@include themify($themes) {
|
||||
border-color: themed('calloutBorderColor');
|
||||
background-color: themed('calloutBackgroundColor');
|
||||
border-left-color: themed("primaryColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
margin-top: 0;
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-info {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("infoColor");
|
||||
}
|
||||
|
||||
h3.callout-heading {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("infoColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-danger {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("dangerColor");
|
||||
}
|
||||
|
||||
&.callout-primary {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed('primaryColor');
|
||||
}
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("dangerColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed('primaryColor');
|
||||
}
|
||||
}
|
||||
&.callout-success {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("successColor");
|
||||
}
|
||||
|
||||
&.callout-info {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed('infoColor');
|
||||
}
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("successColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed('infoColor');
|
||||
}
|
||||
}
|
||||
&.callout-warning {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("warningColor");
|
||||
}
|
||||
|
||||
&.callout-danger {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed('dangerColor');
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed('dangerColor');
|
||||
}
|
||||
}
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("warningColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-success {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed('successColor');
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed('successColor');
|
||||
}
|
||||
}
|
||||
&.clickable {
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.active {
|
||||
@include themify($themes) {
|
||||
background-color: themed("boxBackgroundHoverColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-warning {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed('warningColor');
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed('warningColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.clickable {
|
||||
&:hover, &:focus, &.active {
|
||||
@include themify($themes) {
|
||||
background-color: themed('boxBackgroundHoverColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.enforced-policy-options ul {
|
||||
padding-left: 30px;
|
||||
margin: 0;
|
||||
}
|
||||
.enforced-policy-options ul {
|
||||
padding-left: 30px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="password"]::-ms-reveal {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
display: flex;
|
||||
|
||||
&.flex-grow {
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
&.flex-grow {
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for slow performance on external monitors on Chrome + MacOS
|
||||
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
|
||||
@keyframes redraw {
|
||||
0% {
|
||||
opacity: 0.99;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
0% {
|
||||
opacity: 0.99;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
html.force_redraw {
|
||||
animation: redraw 1s linear infinite;
|
||||
animation: redraw 1s linear infinite;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,13 @@ $border-radius-lg: $border-radius;
|
||||
|
||||
// ref: https://github.com/twbs/bootstrap/blob/v4-dev/scss/_variables.scss
|
||||
|
||||
$grid-breakpoints: ( xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px ) !default;
|
||||
$grid-breakpoints: (
|
||||
xs: 0,
|
||||
sm: 576px,
|
||||
md: 768px,
|
||||
lg: 992px,
|
||||
xl: 1200px,
|
||||
) !default;
|
||||
|
||||
$zindex-modal-backdrop: 1040 !default;
|
||||
$zindex-modal: 1050 !default;
|
||||
@@ -15,19 +21,19 @@ $zindex-modal: 1050 !default;
|
||||
// Padding applied to the modal body
|
||||
$modal-inner-padding: 10px !default;
|
||||
|
||||
$modal-dialog-margin: .5rem !default;
|
||||
$modal-dialog-margin: 0.5rem !default;
|
||||
$modal-dialog-margin-y-sm-up: 1.75rem !default;
|
||||
|
||||
$modal-title-line-height: $line-height-base !default;
|
||||
|
||||
//$modal-content-bg: $background-color-alt !default;
|
||||
$modal-content-border-color: rgba($black, .2) !default;
|
||||
$modal-content-border-color: rgba($black, 0.2) !default;
|
||||
$modal-content-border-width: 1px !default;
|
||||
$modal-content-box-shadow-xs: none;
|
||||
$modal-content-box-shadow-sm-up: none;
|
||||
|
||||
$modal-backdrop-bg: $black !default;
|
||||
$modal-backdrop-opacity: .5 !default;
|
||||
$modal-backdrop-opacity: 0.5 !default;
|
||||
$modal-header-border-color: $border-color-dark !default;
|
||||
$modal-footer-border-color: $modal-header-border-color !default;
|
||||
$modal-header-border-width: $modal-content-border-width !default;
|
||||
@@ -38,7 +44,7 @@ $modal-lg: 800px !default;
|
||||
$modal-md: 500px !default;
|
||||
$modal-sm: 300px !default;
|
||||
|
||||
$modal-transition: transform .3s ease-out !default;
|
||||
$modal-transition: transform 0.3s ease-out !default;
|
||||
|
||||
$close-font-size: $font-size-base * 1.5 !default;
|
||||
$close-font-weight: bold !default;
|
||||
@@ -48,45 +54,44 @@ $close-text-shadow: 0 1px 0 $white !default;
|
||||
// ref: https://github.com/twbs/bootstrap/blob/v4-dev/scss/mixins/_breakpoints.scss
|
||||
|
||||
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
|
||||
$min: breakpoint-min($name, $breakpoints);
|
||||
$min: breakpoint-min($name, $breakpoints);
|
||||
|
||||
@if $min {
|
||||
@media (min-width: $min) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@else {
|
||||
@content;
|
||||
@if $min {
|
||||
@media (min-width: $min) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
|
||||
$min: map-get($breakpoints, $name);
|
||||
@return if($min != 0, $min, null);
|
||||
$min: map-get($breakpoints, $name);
|
||||
@return if($min != 0, $min, null);
|
||||
}
|
||||
|
||||
// Custom Added CSS animations
|
||||
|
||||
@keyframes modalshow {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate(0, -25%);
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate(0, -25%);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes backdropshow {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: $modal-backdrop-opacity;
|
||||
}
|
||||
100% {
|
||||
opacity: $modal-backdrop-opacity;
|
||||
}
|
||||
}
|
||||
|
||||
// ref: https://github.com/twbs/bootstrap/blob/v4-dev/scss/_modal.scss
|
||||
@@ -96,234 +101,234 @@ $close-text-shadow: 0 1px 0 $white !default;
|
||||
// .modal-dialog - positioning shell for the actual modal
|
||||
// .modal-content - actual modal w/ bg and corners and stuff
|
||||
|
||||
|
||||
// Kill the scroll on the body
|
||||
.modal-open {
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Container that the modal scrolls within
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal;
|
||||
//display: none;
|
||||
overflow: hidden;
|
||||
// Prevent Chrome on Windows from adding a focus outline. For details, see
|
||||
// https://github.com/twbs/bootstrap/pull/10951.
|
||||
outline: 0;
|
||||
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
|
||||
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
|
||||
// See also https://github.com/twbs/bootstrap/issues/17695
|
||||
.modal-open & {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal;
|
||||
//display: none;
|
||||
overflow: hidden;
|
||||
// Prevent Chrome on Windows from adding a focus outline. For details, see
|
||||
// https://github.com/twbs/bootstrap/pull/10951.
|
||||
outline: 0;
|
||||
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
|
||||
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
|
||||
// See also https://github.com/twbs/bootstrap/issues/17695
|
||||
.modal-open & {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Shell div to position the modal with bottom padding
|
||||
.modal-dialog {
|
||||
position: relative;
|
||||
width: auto;
|
||||
margin: $modal-dialog-margin;
|
||||
// allow clicks to pass through for custom click handling to close modal
|
||||
pointer-events: none;
|
||||
// When fading in the modal, animate it to slide down
|
||||
.modal.fade & {
|
||||
//@include transition($modal-transition);
|
||||
//transform: translate(0, -25%);
|
||||
animation: modalshow 0.3s ease-in;
|
||||
}
|
||||
//.modal.show & {
|
||||
// transform: translate(0, 0);
|
||||
//}
|
||||
transform: translate(0, 0);
|
||||
position: relative;
|
||||
width: auto;
|
||||
margin: $modal-dialog-margin;
|
||||
// allow clicks to pass through for custom click handling to close modal
|
||||
pointer-events: none;
|
||||
// When fading in the modal, animate it to slide down
|
||||
.modal.fade & {
|
||||
//@include transition($modal-transition);
|
||||
//transform: translate(0, -25%);
|
||||
animation: modalshow 0.3s ease-in;
|
||||
}
|
||||
//.modal.show & {
|
||||
// transform: translate(0, 0);
|
||||
//}
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
|
||||
.modal-dialog-centered {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: calc(100% - (#{$modal-dialog-margin} * 2));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: calc(100% - (#{$modal-dialog-margin} * 2));
|
||||
}
|
||||
|
||||
// Actual modal
|
||||
.modal-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
|
||||
// counteract the pointer-events: none; in the .modal-dialog
|
||||
pointer-events: auto;
|
||||
//background-color: $modal-content-bg;
|
||||
background-clip: padding-box;
|
||||
border: $modal-content-border-width solid $modal-content-border-color;
|
||||
//@include border-radius($border-radius-lg);
|
||||
//@include box-shadow($modal-content-box-shadow-xs);
|
||||
border-radius: $border-radius-lg;
|
||||
box-shadow: $modal-content-box-shadow-xs;
|
||||
// Remove focus outline from opened modal
|
||||
outline: 0;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
|
||||
// counteract the pointer-events: none; in the .modal-dialog
|
||||
pointer-events: auto;
|
||||
//background-color: $modal-content-bg;
|
||||
background-clip: padding-box;
|
||||
border: $modal-content-border-width solid $modal-content-border-color;
|
||||
//@include border-radius($border-radius-lg);
|
||||
//@include box-shadow($modal-content-box-shadow-xs);
|
||||
border-radius: $border-radius-lg;
|
||||
box-shadow: $modal-content-box-shadow-xs;
|
||||
// Remove focus outline from opened modal
|
||||
outline: 0;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('backgroundColorAlt');
|
||||
}
|
||||
@include themify($themes) {
|
||||
background-color: themed("backgroundColorAlt");
|
||||
}
|
||||
}
|
||||
|
||||
// Modal background
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal-backdrop;
|
||||
background-color: $modal-backdrop-bg;
|
||||
// Fade for backdrop
|
||||
&.fade {
|
||||
//opacity: 0;
|
||||
animation: backdropshow 0.1s ease-in;
|
||||
}
|
||||
//&.show {
|
||||
// opacity: $modal-backdrop-opacity;
|
||||
//}
|
||||
opacity: $modal-backdrop-opacity;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal-backdrop;
|
||||
background-color: $modal-backdrop-bg;
|
||||
// Fade for backdrop
|
||||
&.fade {
|
||||
//opacity: 0;
|
||||
animation: backdropshow 0.1s ease-in;
|
||||
}
|
||||
//&.show {
|
||||
// opacity: $modal-backdrop-opacity;
|
||||
//}
|
||||
opacity: $modal-backdrop-opacity;
|
||||
}
|
||||
|
||||
// Modal header
|
||||
// Top section of the modal w/ title and dismiss
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: flex-start; // so the close btn always stays on the upper right corner
|
||||
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
|
||||
display: flex;
|
||||
align-items: flex-start; // so the close btn always stays on the upper right corner
|
||||
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
|
||||
padding: $modal-header-padding $modal-inner-padding;
|
||||
border-bottom: $modal-header-border-width solid $modal-header-border-color;
|
||||
//@include border-top-radius($border-radius-lg);
|
||||
|
||||
@include themify($themes) {
|
||||
border-bottom-color: themed("borderColor");
|
||||
}
|
||||
|
||||
.close {
|
||||
padding: $modal-header-padding $modal-inner-padding;
|
||||
border-bottom: $modal-header-border-width solid $modal-header-border-color;
|
||||
//@include border-top-radius($border-radius-lg);
|
||||
// auto on the left force icon to the right even when there is no .modal-title
|
||||
margin: (-$modal-header-padding) (-$modal-inner-padding) (-$modal-header-padding) auto;
|
||||
}
|
||||
|
||||
@include themify($themes) {
|
||||
border-bottom-color: themed('borderColor');
|
||||
}
|
||||
|
||||
.close {
|
||||
padding: $modal-header-padding $modal-inner-padding;
|
||||
// auto on the left force icon to the right even when there is no .modal-title
|
||||
margin: (-$modal-header-padding) (-$modal-inner-padding) (-$modal-header-padding) auto;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: $font-size-base;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
h5 {
|
||||
font-size: $font-size-base;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Title text within header
|
||||
.modal-title {
|
||||
margin-bottom: 0;
|
||||
line-height: $modal-title-line-height;
|
||||
margin-bottom: 0;
|
||||
line-height: $modal-title-line-height;
|
||||
}
|
||||
|
||||
// Modal body
|
||||
// Where all modal content resides (sibling of .modal-header and .modal-footer)
|
||||
.modal-body {
|
||||
position: relative;
|
||||
// Enable `flex-grow: 1` so that the body take up as much space as possible
|
||||
// when should there be a fixed height on `.modal-dialog`.
|
||||
flex: 1 1 auto;
|
||||
padding: $modal-inner-padding;
|
||||
position: relative;
|
||||
// Enable `flex-grow: 1` so that the body take up as much space as possible
|
||||
// when should there be a fixed height on `.modal-dialog`.
|
||||
flex: 1 1 auto;
|
||||
padding: $modal-inner-padding;
|
||||
}
|
||||
|
||||
// Footer (for actions)
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
align-items: center; // vertically center
|
||||
//justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
|
||||
padding: $modal-inner-padding;
|
||||
border-top: $modal-footer-border-width solid $modal-footer-border-color;
|
||||
|
||||
@include themify($themes) {
|
||||
border-top-color: themed("borderColor");
|
||||
}
|
||||
|
||||
// Easily place margin between footer elements
|
||||
button {
|
||||
margin-right: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center; // vertically center
|
||||
//justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
|
||||
padding: $modal-inner-padding;
|
||||
border-top: $modal-footer-border-width solid $modal-footer-border-color;
|
||||
|
||||
@include themify($themes) {
|
||||
border-top-color: themed('borderColor');
|
||||
}
|
||||
|
||||
// Easily place margin between footer elements
|
||||
button {
|
||||
margin-right: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Measure scrollbar width for padding body during modal show/hide
|
||||
.modal-scrollbar-measure {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
// Scale up the modal
|
||||
@include media-breakpoint-up(sm) {
|
||||
// Automatically set modal's width for larger viewports
|
||||
.modal-dialog {
|
||||
max-width: $modal-md;
|
||||
margin: $modal-dialog-margin-y-sm-up auto;
|
||||
}
|
||||
// Automatically set modal's width for larger viewports
|
||||
.modal-dialog {
|
||||
max-width: $modal-md;
|
||||
margin: $modal-dialog-margin-y-sm-up auto;
|
||||
}
|
||||
|
||||
.modal-dialog-centered {
|
||||
min-height: calc(100% - (#{$modal-dialog-margin-y-sm-up} * 2));
|
||||
}
|
||||
.modal-dialog-centered {
|
||||
min-height: calc(100% - (#{$modal-dialog-margin-y-sm-up} * 2));
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
//@include box-shadow($modal-content-box-shadow-sm-up);
|
||||
box-shadow: $modal-content-box-shadow-sm-up;
|
||||
}
|
||||
.modal-content {
|
||||
//@include box-shadow($modal-content-box-shadow-sm-up);
|
||||
box-shadow: $modal-content-box-shadow-sm-up;
|
||||
}
|
||||
|
||||
.modal-sm {
|
||||
max-width: $modal-sm;
|
||||
}
|
||||
.modal-sm {
|
||||
max-width: $modal-sm;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.modal-lg {
|
||||
max-width: $modal-lg;
|
||||
}
|
||||
.modal-lg {
|
||||
max-width: $modal-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// ref: https://github.com/twbs/bootstrap/blob/v4-dev/scss/_close.scss
|
||||
|
||||
.close {
|
||||
float: right;
|
||||
font-size: $close-font-size;
|
||||
font-weight: $close-font-weight;
|
||||
line-height: 1;
|
||||
color: $close-color;
|
||||
text-shadow: $close-text-shadow;
|
||||
opacity: .5;
|
||||
float: right;
|
||||
font-size: $close-font-size;
|
||||
font-weight: $close-font-weight;
|
||||
line-height: 1;
|
||||
color: $close-color;
|
||||
text-shadow: $close-text-shadow;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover, &:focus {
|
||||
color: $close-color;
|
||||
text-decoration: none;
|
||||
opacity: .75;
|
||||
}
|
||||
// Opinionated: add "hand" cursor to non-disabled .close elements
|
||||
&:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $close-color;
|
||||
text-decoration: none;
|
||||
opacity: 0.75;
|
||||
}
|
||||
// Opinionated: add "hand" cursor to non-disabled .close elements
|
||||
&:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Additional properties for button version
|
||||
@@ -333,19 +338,19 @@ $close-text-shadow: 0 1px 0 $white !default;
|
||||
|
||||
// stylelint-disable property-no-vendor-prefix, selector-no-qualifying-type
|
||||
button.close {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
// stylelint-enable
|
||||
|
||||
// box
|
||||
|
||||
.modal-content .box {
|
||||
margin-top: 20px;
|
||||
margin-top: 20px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +1,97 @@
|
||||
@import "variables.scss";
|
||||
|
||||
app-sync {
|
||||
content {
|
||||
.btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
content {
|
||||
.btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-password-generator .password-block {
|
||||
font-size: $font-size-large;
|
||||
font-family: $font-family-monospace;
|
||||
margin: 20px;
|
||||
font-size: $font-size-large;
|
||||
font-family: $font-family-monospace;
|
||||
margin: 20px;
|
||||
|
||||
.password-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
.password-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
app-home {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.center-content {
|
||||
margin-top: -50px;
|
||||
height: calc(100% + 50px);
|
||||
.center-content {
|
||||
margin-top: -50px;
|
||||
height: calc(100% + 50px);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 284px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.btn + .btn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
a.settings-icon {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("mutedColor");
|
||||
}
|
||||
|
||||
img {
|
||||
width: 284px;
|
||||
margin: 0 auto;
|
||||
&:not(:hover):not(:focus) {
|
||||
span {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.body-sm,
|
||||
body.body-xs {
|
||||
app-home {
|
||||
.center-content {
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.btn + .btn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
a.settings-icon {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('mutedColor');
|
||||
}
|
||||
|
||||
&:not(:hover):not(:focus) {
|
||||
span {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
text-decoration: none;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('primaryColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.body-sm, body.body-xs {
|
||||
app-home {
|
||||
.center-content {
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
margin: 15px 0;
|
||||
}
|
||||
margin: 15px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.body-full {
|
||||
app-home {
|
||||
.center-content {
|
||||
margin-top: -80px;
|
||||
height: calc(100% + 80px);
|
||||
}
|
||||
app-home {
|
||||
.center-content {
|
||||
margin-top: -80px;
|
||||
height: calc(100% + 80px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
$fa-font-path: "~font-awesome/fonts";
|
||||
@import "~font-awesome/scss/font-awesome.scss";
|
||||
@import '~ngx-toastr/toastr';
|
||||
@import "~ngx-toastr/toastr";
|
||||
@import "~sweetalert2/src/sweetalert2.scss";
|
||||
|
||||
@import "variables.scss";
|
||||
@@ -9,204 +9,206 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
// Toaster
|
||||
|
||||
.toast-container {
|
||||
.toast-close-button {
|
||||
font-size: 18px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.ngx-toastr {
|
||||
align-items: center;
|
||||
background-image: none !important;
|
||||
border-radius: $border-radius;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
|
||||
.toast-close-button {
|
||||
font-size: 18px;
|
||||
margin-right: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ngx-toastr {
|
||||
align-items: center;
|
||||
background-image: none !important;
|
||||
border-radius: $border-radius;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
|
||||
.toast-close-button {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.icon i::before {
|
||||
float: left;
|
||||
font-style: normal;
|
||||
font-family: FontAwesome;
|
||||
font-size: 25px;
|
||||
line-height: 20px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-danger, &.toast-error {
|
||||
@include themify($themes) {
|
||||
background-color: themed('dangerColor');
|
||||
}
|
||||
|
||||
.icon i::before {
|
||||
content: "\f0e7";
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-warning {
|
||||
@include themify($themes) {
|
||||
background-color: themed('warningColor');
|
||||
}
|
||||
|
||||
.icon i::before {
|
||||
content: "\f071";
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-info {
|
||||
@include themify($themes) {
|
||||
background-color: themed('infoColor');
|
||||
}
|
||||
|
||||
.icon i:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-success {
|
||||
@include themify($themes) {
|
||||
background-color: themed('successColor');
|
||||
}
|
||||
|
||||
.icon i:before {
|
||||
content: "\f00C";
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.icon i::before {
|
||||
float: left;
|
||||
font-style: normal;
|
||||
font-family: FontAwesome;
|
||||
font-size: 25px;
|
||||
line-height: 20px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-danger,
|
||||
&.toast-error {
|
||||
@include themify($themes) {
|
||||
background-color: themed("dangerColor");
|
||||
}
|
||||
|
||||
.icon i::before {
|
||||
content: "\f0e7";
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-warning {
|
||||
@include themify($themes) {
|
||||
background-color: themed("warningColor");
|
||||
}
|
||||
|
||||
.icon i::before {
|
||||
content: "\f071";
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-info {
|
||||
@include themify($themes) {
|
||||
background-color: themed("infoColor");
|
||||
}
|
||||
|
||||
.icon i:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-success {
|
||||
@include themify($themes) {
|
||||
background-color: themed("successColor");
|
||||
}
|
||||
|
||||
.icon i:before {
|
||||
content: "\f00C";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SweetAlert
|
||||
|
||||
.swal2-popup {
|
||||
padding: 15px;
|
||||
border-radius: $border-radius;
|
||||
width: 34em;
|
||||
padding: 15px;
|
||||
border-radius: $border-radius;
|
||||
width: 34em;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed("backgroundColorAlt");
|
||||
color: themed("textColor");
|
||||
}
|
||||
|
||||
.swal2-icon {
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.swal2-content {
|
||||
margin: 0;
|
||||
font-size: $font-size-base;
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
}
|
||||
|
||||
label.checkbox {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
text-align: left;
|
||||
align-items: top;
|
||||
|
||||
input {
|
||||
margin: 3px 5px 0 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.swal2-input,
|
||||
.swal2-textarea {
|
||||
border: 1px solid #000000;
|
||||
border-radius: $border-radius;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
// Inherit theme font-size
|
||||
font-size: inherit;
|
||||
|
||||
// Sweetalert 1 did not have box-shadow
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
@include themify($themes) {
|
||||
border-color: themed("inputBorderColor");
|
||||
color: themed("textColor");
|
||||
background-color: themed("inputBackgroundColor");
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
@include themify($themes) {
|
||||
color: themed("inputPlaceholderColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i.swal-custom-icon {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.swal2-title {
|
||||
padding: 10px 0 15px;
|
||||
margin: 0;
|
||||
font-size: $font-size-large;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: themed('backgroundColorAlt');
|
||||
color: themed('textColor');
|
||||
color: themed("textColor");
|
||||
}
|
||||
}
|
||||
|
||||
.swal2-icon {
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: none;
|
||||
.swal2-text {
|
||||
text-align: left; // sweetalert1 behaviour
|
||||
font-size: $font-size-base;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
}
|
||||
}
|
||||
|
||||
.swal2-content {
|
||||
margin: 0;
|
||||
font-size: $font-size-base;
|
||||
@include themify($themes) {
|
||||
color: themed('textColor');
|
||||
}
|
||||
> .swal2-text:first-child {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
label.checkbox {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
text-align: left;
|
||||
align-items: top;
|
||||
.swal2-actions {
|
||||
margin: 20px auto 0;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
input {
|
||||
margin: 3px 5px 0 1px;
|
||||
}
|
||||
}
|
||||
button {
|
||||
margin-left: 10px;
|
||||
@extend .btn;
|
||||
|
||||
.swal2-input, .swal2-textarea {
|
||||
border: 1px solid #000000;
|
||||
border-radius: $border-radius;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
// Inherit theme font-size
|
||||
font-size: inherit;
|
||||
|
||||
// Sweetalert 1 did not have box-shadow
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
@include themify($themes) {
|
||||
border-color: themed('inputBorderColor');
|
||||
color: themed('textColor');
|
||||
background-color: themed('inputBackgroundColor');
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
@include themify($themes) {
|
||||
color: themed('inputPlaceholderColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
&.swal2-confirm {
|
||||
@extend .btn, .primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i.swal-custom-icon {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.swal2-title {
|
||||
padding: 10px 0 15px;
|
||||
margin: 0;
|
||||
font-size: $font-size-large;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('textColor');
|
||||
}
|
||||
}
|
||||
|
||||
.swal2-text {
|
||||
text-align: left; // sweetalert1 behaviour
|
||||
font-size: $font-size-base;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed('textColor');
|
||||
}
|
||||
}
|
||||
|
||||
> .swal2-text:first-child {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.swal2-actions {
|
||||
margin: 20px auto 0;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
button {
|
||||
margin-left: 10px;
|
||||
@extend .btn;
|
||||
|
||||
&.swal2-confirm {
|
||||
@extend .btn, .primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.swal2-validation-message {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.swal2-validation-message {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
date-input-polyfill {
|
||||
&[data-open="true"] {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
&[data-open="true"] {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import '~nord/src/sass/nord.scss';
|
||||
@import "~nord/src/sass/nord.scss";
|
||||
|
||||
$font-family-sans-serif: 'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;
|
||||
$font-family-monospace: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
$font-family-sans-serif: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
$font-size-base: 14px;
|
||||
$font-size-large: 18px;
|
||||
$font-size-small: 12px;
|
||||
@@ -18,12 +18,12 @@ $gray: #555;
|
||||
$gray-light: #777;
|
||||
$text-muted: $gray-light;
|
||||
|
||||
$brand-primary: #175DDC;
|
||||
$brand-primary: #175ddc;
|
||||
$brand-danger: #dd4b39;
|
||||
$brand-success: #00a65a;
|
||||
$brand-info: #555555;
|
||||
$brand-warning: #bf7e16;
|
||||
$brand-primary-accent: #1252A3;
|
||||
$brand-primary-accent: #1252a3;
|
||||
|
||||
$background-color: #f0f0f0;
|
||||
|
||||
@@ -37,236 +37,241 @@ $button-color: lighten($text-color, 40%);
|
||||
$button-color-primary: darken($brand-primary, 8%);
|
||||
$button-color-danger: darken($brand-danger, 10%);
|
||||
|
||||
$solarizedDarkBase03: #002b36;
|
||||
$solarizedDarkBase02: #073642;
|
||||
$solarizedDarkBase01: #586e75;
|
||||
$solarizedDarkBase00: #657b83;
|
||||
$solarizedDarkBase0: #839496;
|
||||
$solarizedDarkBase1: #93a1a1;
|
||||
$solarizedDarkBase2: #eee8d5;
|
||||
$solarizedDarkBase3: #fdf6e3;
|
||||
$solarizedDarkYellow: #b58900;
|
||||
$solarizedDarkOrange: #cb4b16;
|
||||
$solarizedDarkRed: #dc322f;
|
||||
$solarizedDarkMagenta: #d33682;
|
||||
$solarizedDarkViolet: #6c71c4;
|
||||
$solarizedDarkBlue: #268bd2;
|
||||
$solarizedDarkCyan: #2aa198;
|
||||
$solarizedDarkGreen: #859900;
|
||||
$solarizedDarkBase03: #002b36;
|
||||
$solarizedDarkBase02: #073642;
|
||||
$solarizedDarkBase01: #586e75;
|
||||
$solarizedDarkBase00: #657b83;
|
||||
$solarizedDarkBase0: #839496;
|
||||
$solarizedDarkBase1: #93a1a1;
|
||||
$solarizedDarkBase2: #eee8d5;
|
||||
$solarizedDarkBase3: #fdf6e3;
|
||||
$solarizedDarkYellow: #b58900;
|
||||
$solarizedDarkOrange: #cb4b16;
|
||||
$solarizedDarkRed: #dc322f;
|
||||
$solarizedDarkMagenta: #d33682;
|
||||
$solarizedDarkViolet: #6c71c4;
|
||||
$solarizedDarkBlue: #268bd2;
|
||||
$solarizedDarkCyan: #2aa198;
|
||||
$solarizedDarkGreen: #859900;
|
||||
|
||||
$themes: (
|
||||
light: (
|
||||
textColor: $text-color,
|
||||
borderColor: $border-color-dark,
|
||||
backgroundColor: $background-color,
|
||||
backgroundColorAlt: #ffffff,
|
||||
scrollbarColor: rgba(100,100,100,.2),
|
||||
scrollbarHoverColor: rgba(100,100,100,.4),
|
||||
boxBackgroundColor: $box-background-color,
|
||||
boxBackgroundHoverColor: $box-background-hover-color,
|
||||
boxBorderColor: $box-border-color,
|
||||
tabBackgroundColor: #ffffff,
|
||||
tabBackgroundHoverColor: $list-item-hover,
|
||||
headerColor: #ffffff,
|
||||
headerBackgroundColor: $brand-primary,
|
||||
headerBackgroundHoverColor: rgba(255, 255, 255, 0.1),
|
||||
headerBorderColor: $brand-primary,
|
||||
headerInputBackgroundColor: darken($brand-primary, 8%),
|
||||
headerInputBackgroundFocusColor: darken($brand-primary, 10%),
|
||||
headerInputColor: #ffffff,
|
||||
headerInputPlaceholderColor: lighten($brand-primary, 35%),
|
||||
listItemBackgroundHoverColor: $list-item-hover,
|
||||
disabledIconColor: $list-icon-color,
|
||||
disabledBoxOpacity: $disabled-box-opacity,
|
||||
headingColor: $gray-light,
|
||||
labelColor: $gray-light,
|
||||
mutedColor: $text-muted,
|
||||
totpStrokeColor: $brand-primary,
|
||||
boxRowButtonColor: $brand-primary,
|
||||
boxRowButtonHoverColor: darken($brand-primary, 10%),
|
||||
inputBorderColor: darken($border-color-dark, 7%),
|
||||
inputBackgroundColor: #ffffff,
|
||||
inputPlaceholderColor: lighten($gray-light, 35%),
|
||||
buttonBackgroundColor: $button-background-color,
|
||||
buttonBorderColor: $button-border-color,
|
||||
buttonColor: $button-color,
|
||||
buttonPrimaryColor: $button-color-primary,
|
||||
buttonDangerColor: $button-color-danger,
|
||||
primaryColor: $brand-primary,
|
||||
primaryAccentColor: $brand-primary-accent,
|
||||
dangerColor: $brand-danger,
|
||||
successColor: $brand-success,
|
||||
infoColor: $brand-info,
|
||||
warningColor: $brand-warning,
|
||||
logoSuffix: 'dark',
|
||||
passwordNumberColor: #007fde,
|
||||
passwordSpecialColor: #c40800,
|
||||
calloutBorderColor: $border-color-dark,
|
||||
calloutBackgroundColor: $box-background-color,
|
||||
),
|
||||
dark: (
|
||||
textColor: #ffffff,
|
||||
borderColor: #111111,
|
||||
backgroundColor: #222222,
|
||||
backgroundColorAlt: #3d3d3d,
|
||||
scrollbarColor: #4d4d4d,
|
||||
scrollbarHoverColor: #5f5f5f,
|
||||
boxBackgroundColor: #363636,
|
||||
boxBackgroundHoverColor: #3f3f3f,
|
||||
boxBorderColor: #2f2f2f,
|
||||
tabBackgroundColor: #363636,
|
||||
tabBackgroundHoverColor: #3f3f3f,
|
||||
headerColor: #ffffff,
|
||||
headerBackgroundColor: #363636,
|
||||
headerBackgroundHoverColor: #3f3f3f,
|
||||
headerBorderColor: #111111,
|
||||
headerInputBackgroundColor: #222222,
|
||||
headerInputBackgroundFocusColor: #1d1d1d,
|
||||
headerInputColor: #ffffff,
|
||||
headerInputPlaceholderColor: #707070,
|
||||
listItemBackgroundHoverColor: #3c3c3c,
|
||||
disabledIconColor: #cacaca,
|
||||
disabledBoxOpacity: 0.5,
|
||||
headingColor: #a3a3a3,
|
||||
labelColor: #a3a3a3,
|
||||
mutedColor: #a3a3a3,
|
||||
totpStrokeColor: #cacaca,
|
||||
boxRowButtonColor: #cacaca,
|
||||
boxRowButtonHoverColor: #ffffff,
|
||||
inputBorderColor: #222222,
|
||||
inputBackgroundColor: #363636,
|
||||
inputPlaceholderColor: #707070,
|
||||
buttonBackgroundColor: #363636,
|
||||
buttonBorderColor: #1f1f1f,
|
||||
buttonColor: #e0e0e0,
|
||||
buttonPrimaryColor: #46ace7,
|
||||
buttonDangerColor: #ff3e24,
|
||||
primaryColor: #52bdfb,
|
||||
primaryAccentColor: #3ea1da,
|
||||
dangerColor: #ff3e24,
|
||||
successColor: $brand-success,
|
||||
infoColor: $brand-info,
|
||||
warningColor: $brand-warning,
|
||||
logoSuffix: 'white',
|
||||
passwordNumberColor: #52bdfb,
|
||||
passwordSpecialColor: #ff7c70,
|
||||
calloutBorderColor: #111111,
|
||||
calloutBackgroundColor: #3d3d3d,
|
||||
),
|
||||
nord: (
|
||||
textColor: $nord5,
|
||||
borderColor: $nord0,
|
||||
backgroundColor: $nord1,
|
||||
backgroundColorAlt: $nord2,
|
||||
scrollbarColor: $nord4,
|
||||
scrollbarHoverColor: $nord6,
|
||||
boxBackgroundColor: $nord2,
|
||||
boxBackgroundHoverColor: $nord3,
|
||||
boxBorderColor: $nord1,
|
||||
tabBackgroundColor: $nord1,
|
||||
tabBackgroundHoverColor: $nord2,
|
||||
headerColor: $nord5,
|
||||
headerBackgroundColor: $nord1,
|
||||
headerBackgroundHoverColor: $nord2,
|
||||
headerBorderColor: $nord0,
|
||||
headerInputBackgroundColor: $nord6,
|
||||
headerInputBackgroundFocusColor: $nord5,
|
||||
headerInputColor: $nord2,
|
||||
headerInputPlaceholderColor: $nord3,
|
||||
listItemBackgroundHoverColor: $nord3,
|
||||
disabledIconColor: $nord4,
|
||||
disabledBoxOpacity: 0.5,
|
||||
headingColor: $nord4,
|
||||
labelColor: $nord4,
|
||||
mutedColor: $nord4,
|
||||
totpStrokeColor: $nord4,
|
||||
boxRowButtonColor: $nord4,
|
||||
boxRowButtonHoverColor: $nord6,
|
||||
inputBorderColor: $nord0,
|
||||
inputBackgroundColor: $nord2,
|
||||
inputPlaceholderColor: lighten($nord3, 20%),
|
||||
buttonBackgroundColor: $nord3,
|
||||
buttonBorderColor: $nord0,
|
||||
buttonColor: $nord5,
|
||||
buttonPrimaryColor: $nord8,
|
||||
buttonDangerColor: $nord11,
|
||||
primaryColor: $nord9,
|
||||
primaryAccentColor: $nord8,
|
||||
dangerColor: $nord11,
|
||||
successColor: $nord14,
|
||||
infoColor: $nord9,
|
||||
warningColor: $nord12,
|
||||
logoSuffix: 'white',
|
||||
passwordNumberColor: $nord8,
|
||||
passwordSpecialColor: $nord12,
|
||||
calloutBorderColor: $nord0,
|
||||
calloutBackgroundColor: $nord2,
|
||||
),
|
||||
solarizedDark: (
|
||||
textColor: $solarizedDarkBase2,
|
||||
borderColor: $solarizedDarkBase03,
|
||||
backgroundColor: $solarizedDarkBase03,
|
||||
backgroundColorAlt: $solarizedDarkBase02,
|
||||
scrollbarColor: $solarizedDarkBase0,
|
||||
scrollbarHoverColor: $solarizedDarkBase2,
|
||||
boxBackgroundColor: $solarizedDarkBase03,
|
||||
boxBackgroundHoverColor: $solarizedDarkBase02,
|
||||
boxBorderColor: $solarizedDarkBase02,
|
||||
tabBackgroundColor: $solarizedDarkBase02,
|
||||
tabBackgroundHoverColor: $solarizedDarkBase01,
|
||||
headerColor: $solarizedDarkBase1,
|
||||
headerBackgroundColor: $solarizedDarkBase02,
|
||||
headerBackgroundHoverColor: $solarizedDarkBase01,
|
||||
headerBorderColor: $solarizedDarkBase03,
|
||||
headerInputBackgroundColor: $solarizedDarkBase2,
|
||||
headerInputBackgroundFocusColor: $solarizedDarkBase1,
|
||||
headerInputColor: $solarizedDarkBase01,
|
||||
headerInputPlaceholderColor: $solarizedDarkBase00,
|
||||
listItemBackgroundHoverColor: $solarizedDarkBase02,
|
||||
disabledIconColor: $solarizedDarkBase0,
|
||||
disabledBoxOpacity: 0.5,
|
||||
headingColor: $solarizedDarkBase0,
|
||||
labelColor: $solarizedDarkBase0,
|
||||
mutedColor: $solarizedDarkBase0,
|
||||
totpStrokeColor: $solarizedDarkBase0,
|
||||
boxRowButtonColor: $solarizedDarkBase0,
|
||||
boxRowButtonHoverColor: $solarizedDarkBase2,
|
||||
inputBorderColor: $solarizedDarkBase03,
|
||||
inputBackgroundColor: $solarizedDarkBase01,
|
||||
inputPlaceholderColor: lighten($solarizedDarkBase00, 20%),
|
||||
buttonBackgroundColor: $solarizedDarkBase00,
|
||||
buttonBorderColor: $solarizedDarkBase03,
|
||||
buttonColor: $solarizedDarkBase1,
|
||||
buttonPrimaryColor: $solarizedDarkCyan,
|
||||
buttonDangerColor: $solarizedDarkRed,
|
||||
primaryColor: $solarizedDarkGreen,
|
||||
primaryAccentColor: $solarizedDarkCyan,
|
||||
dangerColor: $solarizedDarkRed,
|
||||
successColor: $solarizedDarkGreen,
|
||||
infoColor: $solarizedDarkGreen,
|
||||
warningColor: $solarizedDarkYellow,
|
||||
logoSuffix: 'white',
|
||||
passwordNumberColor: $solarizedDarkCyan,
|
||||
passwordSpecialColor: $solarizedDarkYellow,
|
||||
calloutBorderColor: $solarizedDarkBase03,
|
||||
calloutBackgroundColor: $solarizedDarkBase01,
|
||||
),
|
||||
light: (
|
||||
textColor: $text-color,
|
||||
borderColor: $border-color-dark,
|
||||
backgroundColor: $background-color,
|
||||
backgroundColorAlt: #ffffff,
|
||||
scrollbarColor: rgba(100, 100, 100, 0.2),
|
||||
scrollbarHoverColor: rgba(100, 100, 100, 0.4),
|
||||
boxBackgroundColor: $box-background-color,
|
||||
boxBackgroundHoverColor: $box-background-hover-color,
|
||||
boxBorderColor: $box-border-color,
|
||||
tabBackgroundColor: #ffffff,
|
||||
tabBackgroundHoverColor: $list-item-hover,
|
||||
headerColor: #ffffff,
|
||||
headerBackgroundColor: $brand-primary,
|
||||
headerBackgroundHoverColor: rgba(255, 255, 255, 0.1),
|
||||
headerBorderColor: $brand-primary,
|
||||
headerInputBackgroundColor: darken($brand-primary, 8%),
|
||||
headerInputBackgroundFocusColor: darken($brand-primary, 10%),
|
||||
headerInputColor: #ffffff,
|
||||
headerInputPlaceholderColor: lighten($brand-primary, 35%),
|
||||
listItemBackgroundHoverColor: $list-item-hover,
|
||||
disabledIconColor: $list-icon-color,
|
||||
disabledBoxOpacity: $disabled-box-opacity,
|
||||
headingColor: $gray-light,
|
||||
labelColor: $gray-light,
|
||||
mutedColor: $text-muted,
|
||||
totpStrokeColor: $brand-primary,
|
||||
boxRowButtonColor: $brand-primary,
|
||||
boxRowButtonHoverColor: darken($brand-primary, 10%),
|
||||
inputBorderColor: darken($border-color-dark, 7%),
|
||||
inputBackgroundColor: #ffffff,
|
||||
inputPlaceholderColor: lighten($gray-light, 35%),
|
||||
buttonBackgroundColor: $button-background-color,
|
||||
buttonBorderColor: $button-border-color,
|
||||
buttonColor: $button-color,
|
||||
buttonPrimaryColor: $button-color-primary,
|
||||
buttonDangerColor: $button-color-danger,
|
||||
primaryColor: $brand-primary,
|
||||
primaryAccentColor: $brand-primary-accent,
|
||||
dangerColor: $brand-danger,
|
||||
successColor: $brand-success,
|
||||
infoColor: $brand-info,
|
||||
warningColor: $brand-warning,
|
||||
logoSuffix: "dark",
|
||||
passwordNumberColor: #007fde,
|
||||
passwordSpecialColor: #c40800,
|
||||
calloutBorderColor: $border-color-dark,
|
||||
calloutBackgroundColor: $box-background-color,
|
||||
),
|
||||
dark: (
|
||||
textColor: #ffffff,
|
||||
borderColor: #111111,
|
||||
backgroundColor: #222222,
|
||||
backgroundColorAlt: #3d3d3d,
|
||||
scrollbarColor: #4d4d4d,
|
||||
scrollbarHoverColor: #5f5f5f,
|
||||
boxBackgroundColor: #363636,
|
||||
boxBackgroundHoverColor: #3f3f3f,
|
||||
boxBorderColor: #2f2f2f,
|
||||
tabBackgroundColor: #363636,
|
||||
tabBackgroundHoverColor: #3f3f3f,
|
||||
headerColor: #ffffff,
|
||||
headerBackgroundColor: #363636,
|
||||
headerBackgroundHoverColor: #3f3f3f,
|
||||
headerBorderColor: #111111,
|
||||
headerInputBackgroundColor: #222222,
|
||||
headerInputBackgroundFocusColor: #1d1d1d,
|
||||
headerInputColor: #ffffff,
|
||||
headerInputPlaceholderColor: #707070,
|
||||
listItemBackgroundHoverColor: #3c3c3c,
|
||||
disabledIconColor: #cacaca,
|
||||
disabledBoxOpacity: 0.5,
|
||||
headingColor: #a3a3a3,
|
||||
labelColor: #a3a3a3,
|
||||
mutedColor: #a3a3a3,
|
||||
totpStrokeColor: #cacaca,
|
||||
boxRowButtonColor: #cacaca,
|
||||
boxRowButtonHoverColor: #ffffff,
|
||||
inputBorderColor: #222222,
|
||||
inputBackgroundColor: #363636,
|
||||
inputPlaceholderColor: #707070,
|
||||
buttonBackgroundColor: #363636,
|
||||
buttonBorderColor: #1f1f1f,
|
||||
buttonColor: #e0e0e0,
|
||||
buttonPrimaryColor: #46ace7,
|
||||
buttonDangerColor: #ff3e24,
|
||||
primaryColor: #52bdfb,
|
||||
primaryAccentColor: #3ea1da,
|
||||
dangerColor: #ff3e24,
|
||||
successColor: $brand-success,
|
||||
infoColor: $brand-info,
|
||||
warningColor: $brand-warning,
|
||||
logoSuffix: "white",
|
||||
passwordNumberColor: #52bdfb,
|
||||
passwordSpecialColor: #ff7c70,
|
||||
calloutBorderColor: #111111,
|
||||
calloutBackgroundColor: #3d3d3d,
|
||||
),
|
||||
nord: (
|
||||
textColor: $nord5,
|
||||
borderColor: $nord0,
|
||||
backgroundColor: $nord1,
|
||||
backgroundColorAlt: $nord2,
|
||||
scrollbarColor: $nord4,
|
||||
scrollbarHoverColor: $nord6,
|
||||
boxBackgroundColor: $nord2,
|
||||
boxBackgroundHoverColor: $nord3,
|
||||
boxBorderColor: $nord1,
|
||||
tabBackgroundColor: $nord1,
|
||||
tabBackgroundHoverColor: $nord2,
|
||||
headerColor: $nord5,
|
||||
headerBackgroundColor: $nord1,
|
||||
headerBackgroundHoverColor: $nord2,
|
||||
headerBorderColor: $nord0,
|
||||
headerInputBackgroundColor: $nord6,
|
||||
headerInputBackgroundFocusColor: $nord5,
|
||||
headerInputColor: $nord2,
|
||||
headerInputPlaceholderColor: $nord3,
|
||||
listItemBackgroundHoverColor: $nord3,
|
||||
disabledIconColor: $nord4,
|
||||
disabledBoxOpacity: 0.5,
|
||||
headingColor: $nord4,
|
||||
labelColor: $nord4,
|
||||
mutedColor: $nord4,
|
||||
totpStrokeColor: $nord4,
|
||||
boxRowButtonColor: $nord4,
|
||||
boxRowButtonHoverColor: $nord6,
|
||||
inputBorderColor: $nord0,
|
||||
inputBackgroundColor: $nord2,
|
||||
inputPlaceholderColor: lighten($nord3, 20%),
|
||||
buttonBackgroundColor: $nord3,
|
||||
buttonBorderColor: $nord0,
|
||||
buttonColor: $nord5,
|
||||
buttonPrimaryColor: $nord8,
|
||||
buttonDangerColor: $nord11,
|
||||
primaryColor: $nord9,
|
||||
primaryAccentColor: $nord8,
|
||||
dangerColor: $nord11,
|
||||
successColor: $nord14,
|
||||
infoColor: $nord9,
|
||||
warningColor: $nord12,
|
||||
logoSuffix: "white",
|
||||
passwordNumberColor: $nord8,
|
||||
passwordSpecialColor: $nord12,
|
||||
calloutBorderColor: $nord0,
|
||||
calloutBackgroundColor: $nord2,
|
||||
),
|
||||
solarizedDark: (
|
||||
textColor: $solarizedDarkBase2,
|
||||
borderColor: $solarizedDarkBase03,
|
||||
backgroundColor: $solarizedDarkBase03,
|
||||
backgroundColorAlt: $solarizedDarkBase02,
|
||||
scrollbarColor: $solarizedDarkBase0,
|
||||
scrollbarHoverColor: $solarizedDarkBase2,
|
||||
boxBackgroundColor: $solarizedDarkBase03,
|
||||
boxBackgroundHoverColor: $solarizedDarkBase02,
|
||||
boxBorderColor: $solarizedDarkBase02,
|
||||
tabBackgroundColor: $solarizedDarkBase02,
|
||||
tabBackgroundHoverColor: $solarizedDarkBase01,
|
||||
headerColor: $solarizedDarkBase1,
|
||||
headerBackgroundColor: $solarizedDarkBase02,
|
||||
headerBackgroundHoverColor: $solarizedDarkBase01,
|
||||
headerBorderColor: $solarizedDarkBase03,
|
||||
headerInputBackgroundColor: $solarizedDarkBase2,
|
||||
headerInputBackgroundFocusColor: $solarizedDarkBase1,
|
||||
headerInputColor: $solarizedDarkBase01,
|
||||
headerInputPlaceholderColor: $solarizedDarkBase00,
|
||||
listItemBackgroundHoverColor: $solarizedDarkBase02,
|
||||
disabledIconColor: $solarizedDarkBase0,
|
||||
disabledBoxOpacity: 0.5,
|
||||
headingColor: $solarizedDarkBase0,
|
||||
labelColor: $solarizedDarkBase0,
|
||||
mutedColor: $solarizedDarkBase0,
|
||||
totpStrokeColor: $solarizedDarkBase0,
|
||||
boxRowButtonColor: $solarizedDarkBase0,
|
||||
boxRowButtonHoverColor: $solarizedDarkBase2,
|
||||
inputBorderColor: $solarizedDarkBase03,
|
||||
inputBackgroundColor: $solarizedDarkBase01,
|
||||
inputPlaceholderColor: lighten($solarizedDarkBase00, 20%),
|
||||
buttonBackgroundColor: $solarizedDarkBase00,
|
||||
buttonBorderColor: $solarizedDarkBase03,
|
||||
buttonColor: $solarizedDarkBase1,
|
||||
buttonPrimaryColor: $solarizedDarkCyan,
|
||||
buttonDangerColor: $solarizedDarkRed,
|
||||
primaryColor: $solarizedDarkGreen,
|
||||
primaryAccentColor: $solarizedDarkCyan,
|
||||
dangerColor: $solarizedDarkRed,
|
||||
successColor: $solarizedDarkGreen,
|
||||
infoColor: $solarizedDarkGreen,
|
||||
warningColor: $solarizedDarkYellow,
|
||||
logoSuffix: "white",
|
||||
passwordNumberColor: $solarizedDarkCyan,
|
||||
passwordSpecialColor: $solarizedDarkYellow,
|
||||
calloutBorderColor: $solarizedDarkBase03,
|
||||
calloutBackgroundColor: $solarizedDarkBase01,
|
||||
),
|
||||
);
|
||||
|
||||
@mixin themify($themes: $themes) {
|
||||
@each $theme, $map in $themes {
|
||||
html.theme_#{$theme} & {
|
||||
$theme-map: () !global;
|
||||
@each $key, $submap in $map {
|
||||
$value: map-get(map-get($themes, $theme), '#{$key}');
|
||||
$theme-map: map-merge($theme-map, ($key: $value)) !global;
|
||||
}
|
||||
@content;
|
||||
$theme-map: null !global;
|
||||
}
|
||||
@each $theme, $map in $themes {
|
||||
html.theme_#{$theme} & {
|
||||
$theme-map: () !global;
|
||||
@each $key, $submap in $map {
|
||||
$value: map-get(map-get($themes, $theme), "#{$key}");
|
||||
$theme-map: map-merge(
|
||||
$theme-map,
|
||||
(
|
||||
$key: $value,
|
||||
)
|
||||
) !global;
|
||||
}
|
||||
@content;
|
||||
$theme-map: null !global;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@function themed($key) {
|
||||
@return map-get($theme-map, $key);
|
||||
@return map-get($theme-map, $key);
|
||||
}
|
||||
|
||||
@@ -1,129 +1,209 @@
|
||||
<ng-container [formGroup]="datesForm">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="!editMode">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="deletionDate">{{'deletionDate' | i18n}}</label>
|
||||
<select id="deletionDate" name="DeletionDateSelect" formControlName="selectedDeletionDatePreset" required>
|
||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{o.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="selectedDeletionDatePreset.value === 0">
|
||||
<ng-container *ngTemplateOutlet="deletionDateCustom"></ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
||||
<label for="deletionDate">{{'deletionDate' | i18n}}</label>
|
||||
<ng-container *ngTemplateOutlet="deletionDateCustom"></ng-container>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="!editMode">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
|
||||
<select
|
||||
id="deletionDate"
|
||||
name="DeletionDateSelect"
|
||||
formControlName="selectedDeletionDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'deletionDateDesc' | i18n}}
|
||||
<ng-container
|
||||
*ngIf="(!inPopout && browserPath == 'firefox') && (editMode || (selectedDeletionDatePreset.value === 0 && !editMode))">
|
||||
<br>{{'sendFirefoxCustomDatePopoutMessage1' | i18n}} <a
|
||||
(click)="popOutWindow.emit()">{{'sendFirefoxCustomDatePopoutMessage2' | i18n}}</a>
|
||||
{{'sendFirefoxCustomDatePopoutMessage3' | i18n}}
|
||||
</ng-container>
|
||||
<div class="box-content-row" appBoxRow *ngIf="selectedDeletionDatePreset.value === 0">
|
||||
<ng-container *ngTemplateOutlet="deletionDateCustom"></ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
||||
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
|
||||
<ng-container *ngTemplateOutlet="deletionDateCustom"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="!editMode">
|
||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||
<label for="editExpirationDate">{{'expirationDate' | i18n}}</label>
|
||||
<select id="expirationDate" name="ExpirationDateSelect"
|
||||
formControlName="selectedExpirationDatePreset" required>
|
||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{o.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0" appBoxRow>
|
||||
<ng-container *ngTemplateOutlet="expirationDateCustom"></ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box-content-row" *ngIf="editMode" appBoxRow>
|
||||
<div class="flex-label">
|
||||
<label>{{'expirationDate' | i18n}}</label>
|
||||
<button type="button" *ngIf="!disabled" appStopClick (click)="clearExpiration()">
|
||||
{{'clear' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="expirationDateCustom"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'expirationDateDesc' | i18n}}
|
||||
<ng-container
|
||||
*ngIf="(!inPopout && browserPath == 'firefox') && (editMode || (selectedExpirationDatePreset.value === 0 && !editMode))">
|
||||
<br>{{'sendFirefoxCustomDatePopoutMessage1' | i18n}} <a
|
||||
(click)="popOutWindow.emit()">{{'sendFirefoxCustomDatePopoutMessage2' | i18n}}</a>
|
||||
{{'sendFirefoxCustomDatePopoutMessage3' | i18n}}
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "deletionDateDesc" | i18n }}
|
||||
<ng-container
|
||||
*ngIf="
|
||||
!inPopout &&
|
||||
browserPath == 'firefox' &&
|
||||
(editMode || (selectedDeletionDatePreset.value === 0 && !editMode))
|
||||
"
|
||||
>
|
||||
<br />{{ "sendFirefoxCustomDatePopoutMessage1" | i18n }}
|
||||
<a (click)="popOutWindow.emit()">{{ "sendFirefoxCustomDatePopoutMessage2" | i18n }}</a>
|
||||
{{ "sendFirefoxCustomDatePopoutMessage3" | i18n }}
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #deletionDateCustom>
|
||||
<ng-container [ngSwitch]="browserPath">
|
||||
<ng-container *ngSwitchCase="'firefox'">
|
||||
<div class="flex flex-grow">
|
||||
<input id="deletionDateCustomFallback" type="date" name="DeletionDateFallback"
|
||||
formControlName="fallbackDeletionDate" required placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled" data-date-format="mm/dd/yyyy">
|
||||
<input id="deletionTimeCustomFallback" type="time" name="DeletionTimeDate"
|
||||
formControlName="fallbackDeletionTime" required placeholder="HH:MM AM/PM"
|
||||
[readOnly]="disabled">
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'safari'">
|
||||
<div class="flex flex-grow">
|
||||
<input id="deletionDateCustomFallback" type="date" name="DeletionDateFallback"
|
||||
formControlName="fallbackDeletionDate" required placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled" data-date-format="mm/dd/yyyy">
|
||||
<select id="deletionTimeCustomFallback" formControlName="fallbackDeletionTime"
|
||||
name="SafariDeletionTime">
|
||||
<option *ngFor="let o of safariDeletionTimePresetOptions" [ngValue]="o.twentyFourHour">{{o.twelveHour}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<input id="deletionDateCustom" type="datetime-local" name="DeletionDate"
|
||||
formControlName="defaultDeletionDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM">
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #expirationDateCustom>
|
||||
<ng-container [ngSwitch]="browserPath">
|
||||
<ng-container *ngSwitchCase="'firefox'">
|
||||
<div class="flex flex-grow">
|
||||
<input id="expirationDateCustomFallback" type="date" name="ExpirationDateFallback"
|
||||
formControlName="fallbackExpirationDate" [required]="!editMode" placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled" data-date-format="mm/dd/yyyy">
|
||||
<input id="expirationTimeCustomFallback" type="time"
|
||||
name="ExpirationTimeFallback" formControlName="fallbackExpirationTime"
|
||||
[required]="!editMode" placeholder="HH:MM AM/PM" [readOnly]="disabled">
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'safari'">
|
||||
<div class="flex flex-grow">
|
||||
<input id="expirationDateCustomFallback" type="date" name="ExpirationDateFallback"
|
||||
formControlName="fallbackExpirationDate" [required]="!editMode" placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled" data-date-format="mm/dd/yyyy">
|
||||
<select id="expirationTimeCustomFallback"
|
||||
formControlName="fallbackExpirationTime" name="SafariExpirationTime">
|
||||
<option *ngFor="let o of safariExpirationTimePresetOptions" [ngValue]="o.twentyFourHour">
|
||||
{{o.twelveHour}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<input id="expirationDateCustom" type="datetime-local" name="ExpirationDate"
|
||||
formControlName="defaultExpirationDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
[readOnly]="disabled">
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="!editMode">
|
||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||
<label for="editExpirationDate">{{ "expirationDate" | i18n }}</label>
|
||||
<select
|
||||
id="expirationDate"
|
||||
name="ExpirationDateSelect"
|
||||
formControlName="selectedExpirationDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0" appBoxRow>
|
||||
<ng-container *ngTemplateOutlet="expirationDateCustom"></ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box-content-row" *ngIf="editMode" appBoxRow>
|
||||
<div class="flex-label">
|
||||
<label>{{ "expirationDate" | i18n }}</label>
|
||||
<button type="button" *ngIf="!disabled" appStopClick (click)="clearExpiration()">
|
||||
{{ "clear" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="expirationDateCustom"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "expirationDateDesc" | i18n }}
|
||||
<ng-container
|
||||
*ngIf="
|
||||
!inPopout &&
|
||||
browserPath == 'firefox' &&
|
||||
(editMode || (selectedExpirationDatePreset.value === 0 && !editMode))
|
||||
"
|
||||
>
|
||||
<br />{{ "sendFirefoxCustomDatePopoutMessage1" | i18n }}
|
||||
<a (click)="popOutWindow.emit()">{{ "sendFirefoxCustomDatePopoutMessage2" | i18n }}</a>
|
||||
{{ "sendFirefoxCustomDatePopoutMessage3" | i18n }}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #deletionDateCustom>
|
||||
<ng-container [ngSwitch]="browserPath">
|
||||
<ng-container *ngSwitchCase="'firefox'">
|
||||
<div class="flex flex-grow">
|
||||
<input
|
||||
id="deletionDateCustomFallback"
|
||||
type="date"
|
||||
name="DeletionDateFallback"
|
||||
formControlName="fallbackDeletionDate"
|
||||
required
|
||||
placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled"
|
||||
data-date-format="mm/dd/yyyy"
|
||||
/>
|
||||
<input
|
||||
id="deletionTimeCustomFallback"
|
||||
type="time"
|
||||
name="DeletionTimeDate"
|
||||
formControlName="fallbackDeletionTime"
|
||||
required
|
||||
placeholder="HH:MM AM/PM"
|
||||
[readOnly]="disabled"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'safari'">
|
||||
<div class="flex flex-grow">
|
||||
<input
|
||||
id="deletionDateCustomFallback"
|
||||
type="date"
|
||||
name="DeletionDateFallback"
|
||||
formControlName="fallbackDeletionDate"
|
||||
required
|
||||
placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled"
|
||||
data-date-format="mm/dd/yyyy"
|
||||
/>
|
||||
<select
|
||||
id="deletionTimeCustomFallback"
|
||||
formControlName="fallbackDeletionTime"
|
||||
name="SafariDeletionTime"
|
||||
>
|
||||
<option *ngFor="let o of safariDeletionTimePresetOptions" [ngValue]="o.twentyFourHour">
|
||||
{{ o.twelveHour }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<input
|
||||
id="deletionDateCustom"
|
||||
type="datetime-local"
|
||||
name="DeletionDate"
|
||||
formControlName="defaultDeletionDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
/>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #expirationDateCustom>
|
||||
<ng-container [ngSwitch]="browserPath">
|
||||
<ng-container *ngSwitchCase="'firefox'">
|
||||
<div class="flex flex-grow">
|
||||
<input
|
||||
id="expirationDateCustomFallback"
|
||||
type="date"
|
||||
name="ExpirationDateFallback"
|
||||
formControlName="fallbackExpirationDate"
|
||||
[required]="!editMode"
|
||||
placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled"
|
||||
data-date-format="mm/dd/yyyy"
|
||||
/>
|
||||
<input
|
||||
id="expirationTimeCustomFallback"
|
||||
type="time"
|
||||
name="ExpirationTimeFallback"
|
||||
formControlName="fallbackExpirationTime"
|
||||
[required]="!editMode"
|
||||
placeholder="HH:MM AM/PM"
|
||||
[readOnly]="disabled"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'safari'">
|
||||
<div class="flex flex-grow">
|
||||
<input
|
||||
id="expirationDateCustomFallback"
|
||||
type="date"
|
||||
name="ExpirationDateFallback"
|
||||
formControlName="fallbackExpirationDate"
|
||||
[required]="!editMode"
|
||||
placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disabled"
|
||||
data-date-format="mm/dd/yyyy"
|
||||
/>
|
||||
<select
|
||||
id="expirationTimeCustomFallback"
|
||||
formControlName="fallbackExpirationTime"
|
||||
name="SafariExpirationTime"
|
||||
>
|
||||
<option
|
||||
*ngFor="let o of safariExpirationTimePresetOptions"
|
||||
[ngValue]="o.twentyFourHour"
|
||||
>
|
||||
{{ o.twelveHour }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<input
|
||||
id="expirationDateCustom"
|
||||
type="datetime-local"
|
||||
name="ExpirationDate"
|
||||
formControlName="defaultExpirationDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
[readOnly]="disabled"
|
||||
/>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DatePipe } from "@angular/common";
|
||||
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { ControlContainer, NgForm } from "@angular/forms";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { EffluxDatesComponent as BaseEffluxDatesComponent } from 'jslib-angular/components/send/efflux-dates.component';
|
||||
import { EffluxDatesComponent as BaseEffluxDatesComponent } from "jslib-angular/components/send/efflux-dates.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-efflux-dates',
|
||||
templateUrl: 'efflux-dates.component.html',
|
||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
||||
selector: "app-send-efflux-dates",
|
||||
templateUrl: "efflux-dates.component.html",
|
||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
||||
})
|
||||
export class EffluxDatesComponent extends BaseEffluxDatesComponent {
|
||||
@Input() readonly inPopout: boolean;
|
||||
@Output() popOutWindow = new EventEmitter();
|
||||
@Input() readonly inPopout: boolean;
|
||||
@Output() popOutWindow = new EventEmitter();
|
||||
|
||||
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected datePipe: DatePipe) {
|
||||
super(i18nService, platformUtilsService, datePipe);
|
||||
}
|
||||
constructor(
|
||||
protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected datePipe: DatePipe
|
||||
) {
|
||||
super(i18nService, platformUtilsService, datePipe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,222 +1,319 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<button type="button" appBlurClick (click)="cancel()">{{'cancel' | i18n}}</button>
|
||||
<header>
|
||||
<div class="left">
|
||||
<button type="button" appBlurClick (click)="cancel()">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ title }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading || disableSend">
|
||||
<span [hidden]="form.loading">{{ "save" | i18n }}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content *ngIf="send">
|
||||
<!-- Policy Banner -->
|
||||
<app-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
|
||||
{{ "sendDisabledWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout type="info" *ngIf="disableHideEmail && !disableSend">
|
||||
{{ "sendOptionsPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<!-- File Warning -->
|
||||
<app-callout
|
||||
type="warning"
|
||||
icon="fa fa-external-link fa-rotate-270 fa-fw"
|
||||
[clickable]="true"
|
||||
title="{{ 'sendFileCalloutHeader' | i18n }}"
|
||||
*ngIf="showFilePopoutMessage && send.type === sendType.File && !disableSend"
|
||||
(click)="popOutWindow()"
|
||||
>
|
||||
<div *ngIf="showChromiumFileWarning">{{ "sendLinuxChromiumFileWarning" | i18n }}</div>
|
||||
<div *ngIf="showFirefoxFileWarning">{{ "sendFirefoxFileWarning" | i18n }}</div>
|
||||
<div *ngIf="showSafariFileWarning">{{ "sendSafariFileWarning" | i18n }}</div>
|
||||
</app-callout>
|
||||
<!-- Name -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{ "name" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="send.name"
|
||||
[readonly]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{title}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading || disableSend">
|
||||
<span [hidden]="form.loading">{{'save' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "sendNameDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Type Options -->
|
||||
<div class="box" *ngIf="!editMode">
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row">
|
||||
<label for="sendTypeOptions">{{ "sendTypeHeader" | i18n }}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
name="SendTypeOptions"
|
||||
*ngFor="let o of typeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="send.type"
|
||||
name="Type_{{ o.value }}"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="send.type === o.value"
|
||||
[readonly]="disableSend"
|
||||
/>
|
||||
<label for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<content *ngIf="send">
|
||||
<!-- Policy Banner -->
|
||||
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
|
||||
{{'sendDisabledWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" *ngIf="disableHideEmail && !disableSend">
|
||||
{{'sendOptionsPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<!-- File Warning -->
|
||||
<app-callout type="warning" icon="fa fa-external-link fa-rotate-270 fa-fw" [clickable]="true"
|
||||
title="{{'sendFileCalloutHeader' | i18n}}"
|
||||
*ngIf="showFilePopoutMessage && send.type === sendType.File && !disableSend" (click)="popOutWindow()">
|
||||
<div *ngIf="showChromiumFileWarning">{{'sendLinuxChromiumFileWarning' | i18n}}</div>
|
||||
<div *ngIf="showFirefoxFileWarning">{{'sendFirefoxFileWarning' | i18n}}</div>
|
||||
<div *ngIf="showSafariFileWarning">{{'sendSafariFileWarning' | i18n}}</div>
|
||||
</app-callout>
|
||||
<!-- Name -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="send.name" [readonly]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'sendNameDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- File -->
|
||||
<div class="box" *ngIf="send.type === sendType.File && (editMode || showFileSelector)">
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row" *ngIf="editMode">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<div class="row-main">{{ send.file.fileName }} ({{ send.file.sizeName }})</div>
|
||||
</div>
|
||||
<!-- Type Options -->
|
||||
<div class="box" *ngIf="!editMode">
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row">
|
||||
<label for="sendTypeOptions">{{'sendTypeHeader' | i18n}}</label>
|
||||
<div class="radio-group text-default" appBoxRow name="SendTypeOptions"
|
||||
*ngFor="let o of typeOptions">
|
||||
<input type="radio" [(ngModel)]="send.type" name="Type_{{o.value}}" id="type_{{o.value}}"
|
||||
[value]="o.value" (change)="typeChanged()" [checked]="send.type === o.value"
|
||||
[readonly]="disableSend">
|
||||
<label for="type_{{o.value}}">
|
||||
{{o.name}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="showFileSelector">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<input type="file" id="file" name="file" required [readonly]="disableSend" />
|
||||
</div>
|
||||
<!-- File -->
|
||||
<div class="box" *ngIf="send.type === sendType.File && (editMode || showFileSelector)">
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row" *ngIf="editMode">
|
||||
<label for="file">{{'file' | i18n}}</label>
|
||||
<div class="row-main">{{send.file.fileName}} ({{send.file.sizeName}})</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="showFileSelector">
|
||||
<label for="file">{{'file' | i18n}}</label>
|
||||
<input type="file" id="file" name="file" required [readonly]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="showFileSelector">
|
||||
{{'sendFileDesc' | i18n}} {{'maxFileSize' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="showFileSelector">
|
||||
{{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Text -->
|
||||
<div class="box" *ngIf="send.type === sendType.Text">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="text">{{ "sendTypeText" | i18n }}</label>
|
||||
<textarea
|
||||
id="text"
|
||||
name="Text"
|
||||
rows="6"
|
||||
[(ngModel)]="send.text.text"
|
||||
[readonly]="disableSend"
|
||||
></textarea>
|
||||
</div>
|
||||
<!-- Text -->
|
||||
<div class="box" *ngIf="send.type === sendType.Text">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="text">{{'sendTypeText' | i18n}}</label>
|
||||
<textarea id="text" name="Text" rows="6" [(ngModel)]="send.text.text"
|
||||
[readonly]="disableSend"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'sendTextDesc' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideText">{{'sendHideText' | i18n}}</label>
|
||||
<input id="hideText" type="checkbox" name="HideText" [(ngModel)]="send.text.hidden"
|
||||
[disabled]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "sendTextDesc" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideText">{{ "sendHideText" | i18n }}</label>
|
||||
<input
|
||||
id="hideText"
|
||||
type="checkbox"
|
||||
name="HideText"
|
||||
[(ngModel)]="send.text.hidden"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<!-- Share -->
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{'share' | i18n}}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<!-- Copy Link on Save -->
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="copyOnSave">{{'sendShareDesc' | i18n}}</label>
|
||||
<input id="copyOnSave" type="checkbox" name="CopyOnSave" [(ngModel)]="copyLink"
|
||||
[disabled]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Share -->
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{ "share" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<!-- Copy Link on Save -->
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="copyOnSave">{{ "sendShareDesc" | i18n }}</label>
|
||||
<input
|
||||
id="copyOnSave"
|
||||
type="checkbox"
|
||||
name="CopyOnSave"
|
||||
[(ngModel)]="copyLink"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<!-- Options -->
|
||||
<div class="box">
|
||||
<h2>
|
||||
<button type="button" class="box-header-expandable" (click)="showOptions = !showOptions" [attr.aria-expanded]="showOptions">
|
||||
{{'options' | i18n}}
|
||||
<i *ngIf="!showOptions" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showOptions" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Options -->
|
||||
<div class="box">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showOptions = !showOptions"
|
||||
[attr.aria-expanded]="showOptions"
|
||||
>
|
||||
{{ "options" | i18n }}
|
||||
<i *ngIf="!showOptions" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showOptions" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div [hidden]="!showOptions">
|
||||
<app-send-efflux-dates
|
||||
[initialDeletionDate]="send.deletionDate"
|
||||
[initialExpirationDate]="send.expirationDate"
|
||||
[editMode]="editMode"
|
||||
[disabled]="disableSend"
|
||||
(datesChanged)="setDates($event)"
|
||||
(popOutWindow)="popOutWindow()"
|
||||
>
|
||||
</app-send-efflux-dates>
|
||||
<!-- Maximum Access Count -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="maximumAccessCount">{{ "maximumAccessCount" | i18n }}</label>
|
||||
<input
|
||||
id="maximumAccessCount"
|
||||
min="1"
|
||||
type="number"
|
||||
name="MaximumAccessCount"
|
||||
[(ngModel)]="send.maxAccessCount"
|
||||
[readonly]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="!showOptions">
|
||||
<app-send-efflux-dates
|
||||
[initialDeletionDate]="send.deletionDate" [initialExpirationDate]="send.expirationDate"
|
||||
[editMode]="editMode" [disabled]="disableSend" (datesChanged)="setDates($event)" (popOutWindow)="popOutWindow()">
|
||||
</app-send-efflux-dates>
|
||||
<!-- Maximum Access Count -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="maximumAccessCount">{{'maximumAccessCount' | i18n}}</label>
|
||||
<input id="maximumAccessCount" min="1" type="number" name="MaximumAccessCount"
|
||||
[(ngModel)]="send.maxAccessCount" [readonly]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'maximumAccessCountDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Current Access Count -->
|
||||
<div class="box" *ngIf="editMode">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="currentAccessCount">{{'currentAccessCount' | i18n}}</label>
|
||||
<input id="currentAccessCount" readonly type="text" name="CurrentAccessCount"
|
||||
[(ngModel)]="send.accessCount">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Password -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="password" *ngIf="hasPassword">{{'newPassword' | i18n}}</label>
|
||||
<label for="password" *ngIf="!hasPassword">{{'password' | i18n}}</label>
|
||||
<input id="password" type="{{showPassword ? 'text' : 'password'}}" name="Password"
|
||||
class="monospaced" [(ngModel)]="password" appInputVerbatim [readonly]="disableSend">
|
||||
</div>
|
||||
<div class="action-buttons" *ngIf="!disableSend">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePasswordVisible()" [attr.aria-pressed]="showPassword">
|
||||
<i class="fa fa-lg" [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'sendPasswordDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Notes -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notes">{{'notes' | i18n}}</label>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="send.notes"
|
||||
[readonly]="disableSend"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'sendNotesDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Hide Email -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideEmail">{{'hideEmail' | i18n}}</label>
|
||||
<input id="hideEmail" type="checkbox" name="HideEmail" [(ngModel)]="send.hideEmail"
|
||||
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Disable Send -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="disableSend">{{'sendDisableDesc' | i18n}}</label>
|
||||
<input id="disableSend" type="checkbox" name="DisableSend" [(ngModel)]="send.disabled"
|
||||
[disabled]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "maximumAccessCountDesc" | i18n }}
|
||||
</div>
|
||||
<!-- Delete -->
|
||||
<div class="box list" *ngIf="editMode">
|
||||
<div class="box-content single-line">
|
||||
<button type="button" class="box-content-row" appStopClick appBlurClick (click)="delete()"
|
||||
[appApiAction]="deletePromise" #deleteBtn>
|
||||
<div class="row-main text-danger">
|
||||
<div class="icon text-danger" aria-hidden="true">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</div>
|
||||
<span>{{'deleteSend' | i18n}}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Current Access Count -->
|
||||
<div class="box" *ngIf="editMode">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="currentAccessCount">{{ "currentAccessCount" | i18n }}</label>
|
||||
<input
|
||||
id="currentAccessCount"
|
||||
readonly
|
||||
type="text"
|
||||
name="CurrentAccessCount"
|
||||
[(ngModel)]="send.accessCount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</div>
|
||||
<!-- Password -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="password" *ngIf="hasPassword">{{ "newPassword" | i18n }}</label>
|
||||
<label for="password" *ngIf="!hasPassword">{{ "password" | i18n }}</label>
|
||||
<input
|
||||
id="password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="Password"
|
||||
class="monospaced"
|
||||
[(ngModel)]="password"
|
||||
appInputVerbatim
|
||||
[readonly]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons" *ngIf="!disableSend">
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePasswordVisible()"
|
||||
[attr.aria-pressed]="showPassword"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "sendPasswordDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Notes -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notes">{{ "notes" | i18n }}</label>
|
||||
<textarea
|
||||
id="notes"
|
||||
name="Notes"
|
||||
rows="6"
|
||||
[(ngModel)]="send.notes"
|
||||
[readonly]="disableSend"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "sendNotesDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Hide Email -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideEmail">{{ "hideEmail" | i18n }}</label>
|
||||
<input
|
||||
id="hideEmail"
|
||||
type="checkbox"
|
||||
name="HideEmail"
|
||||
[(ngModel)]="send.hideEmail"
|
||||
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Disable Send -->
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="disableSend">{{ "sendDisableDesc" | i18n }}</label>
|
||||
<input
|
||||
id="disableSend"
|
||||
type="checkbox"
|
||||
name="DisableSend"
|
||||
[(ngModel)]="send.disabled"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Delete -->
|
||||
<div class="box list" *ngIf="editMode">
|
||||
<div class="box-content single-line">
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="delete()"
|
||||
[appApiAction]="deletePromise"
|
||||
#deleteBtn
|
||||
>
|
||||
<div class="row-main text-danger">
|
||||
<div class="icon text-danger" aria-hidden="true">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</div>
|
||||
<span>{{ "deleteSend" | i18n }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,128 +1,149 @@
|
||||
import {
|
||||
DatePipe,
|
||||
Location,
|
||||
} from '@angular/common';
|
||||
import { DatePipe, Location } from "@angular/common";
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from 'rxjs/operators';
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SendService } from "jslib-common/abstractions/send.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/send/add-edit.component';
|
||||
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/send/add-edit.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-add-edit',
|
||||
templateUrl: 'send-add-edit.component.html',
|
||||
selector: "app-send-add-edit",
|
||||
templateUrl: "send-add-edit.component.html",
|
||||
})
|
||||
export class SendAddEditComponent extends BaseAddEditComponent {
|
||||
// Options header
|
||||
showOptions = false;
|
||||
// File visibility
|
||||
isFirefox = false;
|
||||
inPopout = false;
|
||||
inSidebar = false;
|
||||
isLinux = false;
|
||||
isUnsupportedMac = false;
|
||||
// Options header
|
||||
showOptions = false;
|
||||
// File visibility
|
||||
isFirefox = false;
|
||||
inPopout = false;
|
||||
inSidebar = false;
|
||||
isLinux = false;
|
||||
isUnsupportedMac = false;
|
||||
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
userService: UserService, messagingService: MessagingService, policyService: PolicyService,
|
||||
environmentService: EnvironmentService, datePipe: DatePipe, sendService: SendService,
|
||||
private route: ActivatedRoute, private router: Router, private location: Location,
|
||||
private popupUtilsService: PopupUtilsService, logService: LogService) {
|
||||
super(i18nService, platformUtilsService, environmentService, datePipe, sendService, userService,
|
||||
messagingService, policyService, logService);
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
userService: UserService,
|
||||
messagingService: MessagingService,
|
||||
policyService: PolicyService,
|
||||
environmentService: EnvironmentService,
|
||||
datePipe: DatePipe,
|
||||
sendService: SendService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
private popupUtilsService: PopupUtilsService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
datePipe,
|
||||
sendService,
|
||||
userService,
|
||||
messagingService,
|
||||
policyService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
|
||||
get showFileSelector(): boolean {
|
||||
return !(this.editMode || this.showFilePopoutMessage);
|
||||
}
|
||||
|
||||
get showFilePopoutMessage(): boolean {
|
||||
return (
|
||||
!this.editMode &&
|
||||
(this.showFirefoxFileWarning || this.showSafariFileWarning || this.showChromiumFileWarning)
|
||||
);
|
||||
}
|
||||
|
||||
get showFirefoxFileWarning(): boolean {
|
||||
return this.isFirefox && !(this.inSidebar || this.inPopout);
|
||||
}
|
||||
|
||||
get showSafariFileWarning(): boolean {
|
||||
return this.isSafari && !this.inPopout;
|
||||
}
|
||||
|
||||
// Only show this for Chromium based browsers in Linux and Mac > Big Sur
|
||||
get showChromiumFileWarning(): boolean {
|
||||
return (
|
||||
(this.isLinux || this.isUnsupportedMac) &&
|
||||
!this.isFirefox &&
|
||||
!(this.inSidebar || this.inPopout)
|
||||
);
|
||||
}
|
||||
|
||||
popOutWindow() {
|
||||
this.popupUtilsService.popOut(window);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// File visilibity
|
||||
this.isFirefox = this.platformUtilsService.isFirefox();
|
||||
this.inPopout = this.popupUtilsService.inPopout(window);
|
||||
this.inSidebar = this.popupUtilsService.inSidebar(window);
|
||||
this.isLinux = window?.navigator?.userAgent.indexOf("Linux") !== -1;
|
||||
this.isUnsupportedMac =
|
||||
this.platformUtilsService.isChrome() && window?.navigator?.appVersion.includes("Mac OS X 11");
|
||||
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
if (params.sendId) {
|
||||
this.sendId = params.sendId;
|
||||
}
|
||||
if (params.type) {
|
||||
const type = parseInt(params.type, null);
|
||||
this.type = type;
|
||||
}
|
||||
await this.load();
|
||||
});
|
||||
|
||||
window.setTimeout(() => {
|
||||
if (!this.editMode) {
|
||||
document.getElementById("name").focus();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
async submit(): Promise<boolean> {
|
||||
if (await super.submit()) {
|
||||
this.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
get showFileSelector(): boolean {
|
||||
return !(this.editMode || this.showFilePopoutMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
async delete(): Promise<boolean> {
|
||||
if (await super.delete()) {
|
||||
this.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
get showFilePopoutMessage(): boolean {
|
||||
return !this.editMode && (this.showFirefoxFileWarning || this.showSafariFileWarning || this.showChromiumFileWarning);
|
||||
}
|
||||
|
||||
get showFirefoxFileWarning(): boolean {
|
||||
return this.isFirefox && !(this.inSidebar || this.inPopout);
|
||||
}
|
||||
|
||||
get showSafariFileWarning(): boolean {
|
||||
return this.isSafari && !this.inPopout;
|
||||
}
|
||||
|
||||
// Only show this for Chromium based browsers in Linux and Mac > Big Sur
|
||||
get showChromiumFileWarning(): boolean {
|
||||
return (this.isLinux || this.isUnsupportedMac) && !this.isFirefox && !(this.inSidebar || this.inPopout);
|
||||
}
|
||||
|
||||
popOutWindow() {
|
||||
this.popupUtilsService.popOut(window);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// File visilibity
|
||||
this.isFirefox = this.platformUtilsService.isFirefox();
|
||||
this.inPopout = this.popupUtilsService.inPopout(window);
|
||||
this.inSidebar = this.popupUtilsService.inSidebar(window);
|
||||
this.isLinux = window?.navigator?.userAgent.indexOf('Linux') !== -1;
|
||||
this.isUnsupportedMac = this.platformUtilsService.isChrome() && window?.navigator?.appVersion.includes('Mac OS X 11');
|
||||
|
||||
this.route.queryParams.pipe(first()).subscribe(async params => {
|
||||
if (params.sendId) {
|
||||
this.sendId = params.sendId;
|
||||
}
|
||||
if (params.type) {
|
||||
const type = parseInt(params.type, null);
|
||||
this.type = type;
|
||||
}
|
||||
await this.load();
|
||||
});
|
||||
|
||||
window.setTimeout(() => {
|
||||
if (!this.editMode) {
|
||||
document.getElementById('name').focus();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
async submit(): Promise<boolean> {
|
||||
if (await super.submit()) {
|
||||
this.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async delete(): Promise<boolean> {
|
||||
if (await super.delete()) {
|
||||
this.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
// If true, the window was pop'd out on the add-send page. location.back will not work
|
||||
if ((window as any).previousPopupUrl.startsWith('/add-send')) {
|
||||
this.router.navigate(['tabs/send']);
|
||||
} else {
|
||||
this.location.back();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
// If true, the window was pop'd out on the add-send page. location.back will not work
|
||||
if ((window as any).previousPopupUrl.startsWith("/add-send")) {
|
||||
this.router.navigate(["tabs/send"]);
|
||||
} else {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,122 @@
|
||||
<header>
|
||||
<div class="left" *ngIf="showLeftHeader">
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
<h1 class="sr-only">{{'send' | i18n}}</h1>
|
||||
<div class="search">
|
||||
<input type="search" placeholder="{{'searchSends' | i18n}}" id="search" [(ngModel)]="searchText"
|
||||
(input)="search(200)" autocomplete="off" appAutofocus>
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button type="button" appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}" [disabled]="disableSend">
|
||||
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="left" *ngIf="showLeftHeader">
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
<h1 class="sr-only">{{ "send" | i18n }}</h1>
|
||||
<div class="search">
|
||||
<input
|
||||
type="search"
|
||||
placeholder="{{ 'searchSends' | i18n }}"
|
||||
id="search"
|
||||
[(ngModel)]="searchText"
|
||||
(input)="search(200)"
|
||||
autocomplete="off"
|
||||
appAutofocus
|
||||
/>
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button
|
||||
type="button"
|
||||
appBlurClick
|
||||
(click)="addSend()"
|
||||
appA11yTitle="{{ 'addSend' | i18n }}"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content [ngClass]="{'flex' : disableSend, 'tab-page' : disableSend}">
|
||||
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
|
||||
{{'sendDisabledWarning' | i18n}}
|
||||
</app-callout>
|
||||
<div class="no-items" *ngIf="(!sends || !sends.length) && !showSearching()">
|
||||
<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 type="button" (click)="addSend()" class="btn block primary link"
|
||||
[disabled]="disableSend">{{'addSend' | i18n}}</button>
|
||||
</ng-container>
|
||||
<content [ngClass]="{ flex: disableSend, 'tab-page': disableSend }">
|
||||
<app-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
|
||||
{{ "sendDisabledWarning" | i18n }}
|
||||
</app-callout>
|
||||
<div class="no-items" *ngIf="(!sends || !sends.length) && !showSearching()">
|
||||
<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
|
||||
type="button"
|
||||
(click)="addSend()"
|
||||
class="btn block primary link"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
{{ "addSend" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-container *ngIf="sends && sends.length && !showSearching()">
|
||||
<div class="box list">
|
||||
<h2 class="box-header">
|
||||
{{ "types" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content single-line">
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="selectType(sendType.Text)"
|
||||
>
|
||||
<div class="row-main">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-file-text-o"></i></div>
|
||||
<span class="text">{{ "sendTypeText" | i18n }}</span>
|
||||
</div>
|
||||
<span class="row-sub-label">{{ typeCounts.get(sendType.Text) || 0 }}</span>
|
||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon"></i></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="selectType(sendType.File)"
|
||||
>
|
||||
<div class="row-main">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-file-o"></i></div>
|
||||
<span class="text">{{ "sendTypeFile" | i18n }}</span>
|
||||
</div>
|
||||
<span class="row-sub-label">{{ typeCounts.get(sendType.File) || 0 }}</span>
|
||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="sends && sends.length && !showSearching()">
|
||||
<div class="box list">
|
||||
<h2 class="box-header">
|
||||
{{'types' | i18n}}
|
||||
</h2>
|
||||
<div class="box-content single-line">
|
||||
<button type="button" class="box-content-row" appStopClick appBlurClick (click)="selectType(sendType.Text)">
|
||||
<div class="row-main">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-file-text-o"></i></div>
|
||||
<span class="text">{{'sendTypeText' | i18n}}</span>
|
||||
</div>
|
||||
<span class="row-sub-label">{{typeCounts.get(sendType.Text) || 0}}</span>
|
||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon"></i></span>
|
||||
</button>
|
||||
<button type="button" class="box-content-row" appStopClick appBlurClick (click)="selectType(sendType.File)">
|
||||
<div class="row-main">
|
||||
<div class="icon"><i class="fa fa-fw fa-lg fa-file-o"></i></div>
|
||||
<span class="text">{{'sendTypeFile' | i18n}}</span>
|
||||
</div>
|
||||
<span class="row-sub-label">{{typeCounts.get(sendType.File) || 0}}</span>
|
||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">
|
||||
{{'allSends' | i18n}}
|
||||
<div class="flex-right">{{sends.length}}</div>
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<app-send-list [sends]="sends" title="{{'editItem' | i18n}}" [disabledByPolicy]="disableSend"
|
||||
(onSelected)="selectSend($event)" (onCopySendLink)="copy($event)"
|
||||
(onRemovePassword)="removePassword($event)" (onDeleteSend)="delete($event)"></app-send-list>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showSearching()">
|
||||
<div class="no-items" *ngIf="!filteredSends || !filteredSends.length">
|
||||
<p>{{'noItemsInList' | i18n}}</p>
|
||||
</div>
|
||||
<div class="box list full-list" *ngIf="filteredSends && filteredSends.length > 0">
|
||||
<div class="box-content">
|
||||
<app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" [disabledByPolicy]="disableSend"
|
||||
(onSelected)="selectSend($event)" (onCopySendLink)="copy($event)"
|
||||
(onRemovePassword)="removePassword($event)" (onDeleteSend)="delete($event)">
|
||||
</app-send-list>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">
|
||||
{{ "allSends" | i18n }}
|
||||
<div class="flex-right">{{ sends.length }}</div>
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<app-send-list
|
||||
[sends]="sends"
|
||||
title="{{ 'editItem' | i18n }}"
|
||||
[disabledByPolicy]="disableSend"
|
||||
(onSelected)="selectSend($event)"
|
||||
(onCopySendLink)="copy($event)"
|
||||
(onRemovePassword)="removePassword($event)"
|
||||
(onDeleteSend)="delete($event)"
|
||||
></app-send-list>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showSearching()">
|
||||
<div class="no-items" *ngIf="!filteredSends || !filteredSends.length">
|
||||
<p>{{ "noItemsInList" | i18n }}</p>
|
||||
</div>
|
||||
<div class="box list full-list" *ngIf="filteredSends && filteredSends.length > 0">
|
||||
<div class="box-content">
|
||||
<app-send-list
|
||||
[sends]="filteredSends"
|
||||
title="{{ 'editItem' | i18n }}"
|
||||
[disabledByPolicy]="disableSend"
|
||||
(onSelected)="selectSend($event)"
|
||||
(onCopySendLink)="copy($event)"
|
||||
(onRemovePassword)="removePassword($event)"
|
||||
(onDeleteSend)="delete($event)"
|
||||
>
|
||||
</app-send-list>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</content>
|
||||
|
||||
@@ -1,188 +1,205 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, NgZone } from "@angular/core";
|
||||
|
||||
import {
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { SendView } from 'jslib-common/models/view/sendView';
|
||||
import { SendView } from "jslib-common/models/view/sendView";
|
||||
|
||||
import { SendComponent as BaseSendComponent } from 'jslib-angular/components/send/send.component';
|
||||
import { SendComponent as BaseSendComponent } from "jslib-angular/components/send/send.component";
|
||||
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { SendService } from "jslib-common/abstractions/send.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
import { SendType } from 'jslib-common/enums/sendType';
|
||||
import { SendType } from "jslib-common/enums/sendType";
|
||||
|
||||
const ComponentId = 'SendComponent';
|
||||
const ScopeStateId = ComponentId + 'Scope';
|
||||
const ComponentId = "SendComponent";
|
||||
const ScopeStateId = ComponentId + "Scope";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-groupings',
|
||||
templateUrl: 'send-groupings.component.html',
|
||||
selector: "app-send-groupings",
|
||||
templateUrl: "send-groupings.component.html",
|
||||
})
|
||||
export class SendGroupingsComponent extends BaseSendComponent {
|
||||
// Header
|
||||
showLeftHeader = true;
|
||||
// Send Type Calculations
|
||||
typeCounts = new Map<SendType, number>();
|
||||
// State Handling
|
||||
state: any;
|
||||
scopeState: any;
|
||||
private loadedTimeout: number;
|
||||
// Header
|
||||
showLeftHeader = true;
|
||||
// Send Type Calculations
|
||||
typeCounts = new Map<SendType, number>();
|
||||
// State Handling
|
||||
state: any;
|
||||
scopeState: any;
|
||||
private loadedTimeout: number;
|
||||
|
||||
constructor(sendService: SendService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService, ngZone: NgZone,
|
||||
policyService: PolicyService, userService: UserService, searchService: SearchService,
|
||||
private popupUtils: PopupUtilsService, private stateService: StateService,
|
||||
private router: Router, private syncService: SyncService,
|
||||
private changeDetectorRef: ChangeDetectorRef, private broadcasterService: BroadcasterService,
|
||||
logService: LogService) {
|
||||
super(sendService, i18nService, platformUtilsService, environmentService, ngZone, searchService,
|
||||
policyService, userService, logService);
|
||||
super.onSuccessfulLoad = async () => {
|
||||
this.calculateTypeCounts();
|
||||
this.selectAll();
|
||||
};
|
||||
constructor(
|
||||
sendService: SendService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
ngZone: NgZone,
|
||||
policyService: PolicyService,
|
||||
userService: UserService,
|
||||
searchService: SearchService,
|
||||
private popupUtils: PopupUtilsService,
|
||||
private stateService: StateService,
|
||||
private router: Router,
|
||||
private syncService: SyncService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private broadcasterService: BroadcasterService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
sendService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
ngZone,
|
||||
searchService,
|
||||
policyService,
|
||||
userService,
|
||||
logService
|
||||
);
|
||||
super.onSuccessfulLoad = async () => {
|
||||
this.calculateTypeCounts();
|
||||
this.selectAll();
|
||||
};
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// Determine Header details
|
||||
this.showLeftHeader = !(
|
||||
this.popupUtils.inSidebar(window) && this.platformUtilsService.isFirefox()
|
||||
);
|
||||
// Clear state of Send Type Component
|
||||
this.stateService.remove("SendTypeComponent");
|
||||
// Let super class finish
|
||||
await super.ngOnInit();
|
||||
// Handle State Restore if necessary
|
||||
const restoredScopeState = await this.restoreState();
|
||||
this.state = (await this.stateService.get<any>(ComponentId)) || {};
|
||||
if (this.state.searchText != null) {
|
||||
this.searchText = this.state.searchText;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// Determine Header details
|
||||
this.showLeftHeader = !(this.popupUtils.inSidebar(window) && this.platformUtilsService.isFirefox());
|
||||
// Clear state of Send Type Component
|
||||
this.stateService.remove('SendTypeComponent');
|
||||
// Let super class finish
|
||||
await super.ngOnInit();
|
||||
// Handle State Restore if necessary
|
||||
const restoredScopeState = await this.restoreState();
|
||||
this.state = (await this.stateService.get<any>(ComponentId)) || {};
|
||||
if (this.state.searchText != null) {
|
||||
this.searchText = this.state.searchText;
|
||||
if (!this.syncService.syncInProgress) {
|
||||
this.load();
|
||||
} else {
|
||||
this.loadedTimeout = window.setTimeout(() => {
|
||||
if (!this.loaded) {
|
||||
this.load();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (!this.syncService.syncInProgress || restoredScopeState) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (!this.syncService.syncInProgress) {
|
||||
this.load();
|
||||
} else {
|
||||
this.loadedTimeout = window.setTimeout(() => {
|
||||
if (!this.loaded) {
|
||||
this.load();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.syncService.syncInProgress || restoredScopeState) {
|
||||
window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
// Remove timeout
|
||||
if (this.loadedTimeout != null) {
|
||||
window.clearTimeout(this.loadedTimeout);
|
||||
}
|
||||
// Save state
|
||||
this.saveState();
|
||||
// Unsubscribe
|
||||
this.broadcasterService.unsubscribe(ComponentId);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
async selectType(type: SendType) {
|
||||
this.router.navigate(["/send-type"], { queryParams: { type: type } });
|
||||
}
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
});
|
||||
async selectSend(s: SendView) {
|
||||
this.router.navigate(["/edit-send"], { queryParams: { sendId: s.id } });
|
||||
}
|
||||
|
||||
async addSend() {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
}
|
||||
this.router.navigate(["/add-send"]);
|
||||
}
|
||||
|
||||
async removePassword(s: SendView): Promise<boolean> {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
}
|
||||
super.removePassword(s);
|
||||
}
|
||||
|
||||
showSearching() {
|
||||
return (
|
||||
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))
|
||||
);
|
||||
}
|
||||
|
||||
private calculateTypeCounts() {
|
||||
// Create type counts
|
||||
const typeCounts = new Map<SendType, number>();
|
||||
this.sends.forEach((s) => {
|
||||
if (typeCounts.has(s.type)) {
|
||||
typeCounts.set(s.type, typeCounts.get(s.type) + 1);
|
||||
} else {
|
||||
typeCounts.set(s.type, 1);
|
||||
}
|
||||
});
|
||||
this.typeCounts = typeCounts;
|
||||
}
|
||||
|
||||
private async saveState() {
|
||||
this.state = {
|
||||
scrollY: this.popupUtils.getContentScrollY(window),
|
||||
searchText: this.searchText,
|
||||
};
|
||||
await this.stateService.save(ComponentId, this.state);
|
||||
|
||||
this.scopeState = {
|
||||
sends: this.sends,
|
||||
typeCounts: this.typeCounts,
|
||||
};
|
||||
await this.stateService.save(ScopeStateId, this.scopeState);
|
||||
}
|
||||
|
||||
private async restoreState(): Promise<boolean> {
|
||||
this.scopeState = await this.stateService.get<any>(ScopeStateId);
|
||||
if (this.scopeState == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// Remove timeout
|
||||
if (this.loadedTimeout != null) {
|
||||
window.clearTimeout(this.loadedTimeout);
|
||||
}
|
||||
// Save state
|
||||
this.saveState();
|
||||
// Unsubscribe
|
||||
this.broadcasterService.unsubscribe(ComponentId);
|
||||
if (this.scopeState.sends != null) {
|
||||
this.sends = this.scopeState.sends;
|
||||
}
|
||||
if (this.scopeState.typeCounts != null) {
|
||||
this.typeCounts = this.scopeState.typeCounts;
|
||||
}
|
||||
|
||||
async selectType(type: SendType) {
|
||||
this.router.navigate(['/send-type'], { queryParams: { type: type } });
|
||||
}
|
||||
|
||||
async selectSend(s: SendView) {
|
||||
this.router.navigate(['/edit-send'], { queryParams: { sendId: s.id } });
|
||||
}
|
||||
|
||||
async addSend() {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
}
|
||||
this.router.navigate(['/add-send']);
|
||||
}
|
||||
|
||||
async removePassword(s: SendView): Promise<boolean> {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
}
|
||||
super.removePassword(s);
|
||||
}
|
||||
|
||||
showSearching() {
|
||||
return this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText));
|
||||
}
|
||||
|
||||
private calculateTypeCounts() {
|
||||
// Create type counts
|
||||
const typeCounts = new Map<SendType, number>();
|
||||
this.sends.forEach(s => {
|
||||
if (typeCounts.has(s.type)) {
|
||||
typeCounts.set(s.type, typeCounts.get(s.type) + 1);
|
||||
} else {
|
||||
typeCounts.set(s.type, 1);
|
||||
}
|
||||
});
|
||||
this.typeCounts = typeCounts;
|
||||
}
|
||||
|
||||
private async saveState() {
|
||||
this.state = {
|
||||
scrollY: this.popupUtils.getContentScrollY(window),
|
||||
searchText: this.searchText,
|
||||
};
|
||||
await this.stateService.save(ComponentId, this.state);
|
||||
|
||||
this.scopeState = {
|
||||
sends: this.sends,
|
||||
typeCounts: this.typeCounts,
|
||||
};
|
||||
await this.stateService.save(ScopeStateId, this.scopeState);
|
||||
}
|
||||
|
||||
private async restoreState(): Promise<boolean> {
|
||||
this.scopeState = await this.stateService.get<any>(ScopeStateId);
|
||||
if (this.scopeState == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.scopeState.sends != null) {
|
||||
this.sends = this.scopeState.sends;
|
||||
}
|
||||
if (this.scopeState.typeCounts != null) {
|
||||
this.typeCounts = this.scopeState.typeCounts;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,69 @@
|
||||
<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>
|
||||
<h1 class="sr-only">{{'send' | i18n}}</h1>
|
||||
<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 type="button" appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}" [disabled]="disableSend">
|
||||
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<h1 class="sr-only">{{ "send" | i18n }}</h1>
|
||||
<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
|
||||
type="button"
|
||||
appBlurClick
|
||||
(click)="addSend()"
|
||||
appA11yTitle="{{ 'addSend' | i18n }}"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content [ngClass]="{'flex' : disableSend}">
|
||||
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
|
||||
{{'sendDisabledWarning' | i18n}}
|
||||
</app-callout>
|
||||
<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 type="button" (click)="addSend()" class="btn block primary link" [disabled]="disableSend">
|
||||
{{'addSend' | i18n}}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="box list only-list" *ngIf="filteredSends.length">
|
||||
<h2 class="box-header">
|
||||
{{groupingTitle}}
|
||||
<span class="flex-right">{{filteredSends.length}}</span>
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" [disabledByPolicy]="disableSend"
|
||||
(onSelected)="selectSend($event)" (onCopySendLink)="copy($event)"
|
||||
(onRemovePassword)="removePassword($event)" (onDeleteSend)="delete($event)">
|
||||
</app-send-list>
|
||||
</div>
|
||||
<content [ngClass]="{ flex: disableSend }">
|
||||
<app-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
|
||||
{{ "sendDisabledWarning" | i18n }}
|
||||
</app-callout>
|
||||
<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
|
||||
type="button"
|
||||
(click)="addSend()"
|
||||
class="btn block primary link"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
{{ "addSend" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="box list only-list" *ngIf="filteredSends.length">
|
||||
<h2 class="box-header">
|
||||
{{ groupingTitle }}
|
||||
<span class="flex-right">{{ filteredSends.length }}</span>
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<app-send-list
|
||||
[sends]="filteredSends"
|
||||
title="{{ 'editItem' | i18n }}"
|
||||
[disabledByPolicy]="disableSend"
|
||||
(onSelected)="selectSend($event)"
|
||||
(onCopySendLink)="copy($event)"
|
||||
(onRemovePassword)="removePassword($event)"
|
||||
(onDeleteSend)="delete($event)"
|
||||
>
|
||||
</app-send-list>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,157 +1,172 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, NgZone } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from 'rxjs/operators';
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { Location } from '@angular/common';
|
||||
import { Location } from "@angular/common";
|
||||
|
||||
import { SendView } from 'jslib-common/models/view/sendView';
|
||||
import { SendView } from "jslib-common/models/view/sendView";
|
||||
|
||||
import { SendComponent as BaseSendComponent } from 'jslib-angular/components/send/send.component';
|
||||
import { SendComponent as BaseSendComponent } from "jslib-angular/components/send/send.component";
|
||||
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { SendService } from "jslib-common/abstractions/send.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
import { SendType } from 'jslib-common/enums/sendType';
|
||||
import { SendType } from "jslib-common/enums/sendType";
|
||||
|
||||
const ComponentId = 'SendTypeComponent';
|
||||
const ComponentId = "SendTypeComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-type',
|
||||
templateUrl: 'send-type.component.html',
|
||||
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;
|
||||
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, private router: Router, logService: LogService) {
|
||||
super(sendService, i18nService, platformUtilsService, environmentService, ngZone, searchService,
|
||||
policyService, userService, logService);
|
||||
super.onSuccessfulLoad = async () => {
|
||||
this.selectType(this.type);
|
||||
};
|
||||
this.applySavedState = (window as any).previousPopupUrl != null &&
|
||||
!(window as any).previousPopupUrl.startsWith('/send-type');
|
||||
}
|
||||
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,
|
||||
private router: Router,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
sendService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
ngZone,
|
||||
searchService,
|
||||
policyService,
|
||||
userService,
|
||||
logService
|
||||
);
|
||||
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();
|
||||
this.route.queryParams.pipe(first()).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);
|
||||
});
|
||||
|
||||
// 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);
|
||||
async ngOnInit() {
|
||||
// Let super class finish
|
||||
await super.ngOnInit();
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
if (this.applySavedState) {
|
||||
this.state = (await this.stateService.get<any>(ComponentId)) || {};
|
||||
if (this.state.searchText != null) {
|
||||
this.searchText = this.state.searchText;
|
||||
}
|
||||
// Save state
|
||||
this.saveState();
|
||||
// Unsubscribe
|
||||
this.broadcasterService.unsubscribe(ComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
async selectSend(s: SendView) {
|
||||
this.router.navigate(['/edit-send'], { queryParams: { sendId: s.id } });
|
||||
}
|
||||
|
||||
async addSend() {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
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;
|
||||
}
|
||||
this.router.navigate(['/add-send'], { queryParams: { type: this.type } });
|
||||
}
|
||||
await this.load((s) => s.type === this.type);
|
||||
}
|
||||
|
||||
async removePassword(s: SendView): Promise<boolean> {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
// 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);
|
||||
});
|
||||
|
||||
// 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;
|
||||
}
|
||||
super.removePassword(s);
|
||||
}
|
||||
|
||||
back() {
|
||||
(window as any).routeDirection = 'b';
|
||||
this.location.back();
|
||||
}
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async saveState() {
|
||||
this.state = {
|
||||
scrollY: this.popupUtils.getContentScrollY(window),
|
||||
searchText: this.searchText,
|
||||
};
|
||||
await this.stateService.save(ComponentId, this.state);
|
||||
ngOnDestroy() {
|
||||
// Remove timeout
|
||||
if (this.refreshTimeout != null) {
|
||||
window.clearTimeout(this.refreshTimeout);
|
||||
}
|
||||
// Save state
|
||||
this.saveState();
|
||||
// Unsubscribe
|
||||
this.broadcasterService.unsubscribe(ComponentId);
|
||||
}
|
||||
|
||||
async selectSend(s: SendView) {
|
||||
this.router.navigate(["/edit-send"], { queryParams: { sendId: s.id } });
|
||||
}
|
||||
|
||||
async addSend() {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
}
|
||||
this.router.navigate(["/add-send"], { queryParams: { type: this.type } });
|
||||
}
|
||||
|
||||
async removePassword(s: SendView): Promise<boolean> {
|
||||
if (this.disableSend) {
|
||||
return;
|
||||
}
|
||||
super.removePassword(s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
OnDestroy
|
||||
} from '@angular/core';
|
||||
import {
|
||||
CanActivate,
|
||||
NavigationEnd,
|
||||
NavigationStart,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { Injectable, OnDestroy } from "@angular/core";
|
||||
import { CanActivate, NavigationEnd, NavigationStart, Router } from "@angular/router";
|
||||
|
||||
import { Subscription } from 'rxjs';
|
||||
import {
|
||||
filter,
|
||||
pairwise,
|
||||
} from 'rxjs/operators';
|
||||
import { Subscription } from "rxjs";
|
||||
import { filter, pairwise } from "rxjs/operators";
|
||||
|
||||
@Injectable()
|
||||
export class DebounceNavigationService implements CanActivate, OnDestroy {
|
||||
navigationStartSub: Subscription;
|
||||
navigationSuccessSub: Subscription;
|
||||
navigationStartSub: Subscription;
|
||||
navigationSuccessSub: Subscription;
|
||||
|
||||
private lastNavigation: NavigationStart;
|
||||
private thisNavigation: NavigationStart;
|
||||
private lastNavigationSuccessId: number;
|
||||
private lastNavigation: NavigationStart;
|
||||
private thisNavigation: NavigationStart;
|
||||
private lastNavigationSuccessId: number;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.navigationStartSub = this.router.events
|
||||
.pipe(filter(event => event instanceof NavigationStart), pairwise())
|
||||
.subscribe((events: [NavigationStart, NavigationStart]) => [this.lastNavigation, this.thisNavigation] = events);
|
||||
constructor(private router: Router) {
|
||||
this.navigationStartSub = this.router.events
|
||||
.pipe(
|
||||
filter((event) => event instanceof NavigationStart),
|
||||
pairwise()
|
||||
)
|
||||
.subscribe(
|
||||
(events: [NavigationStart, NavigationStart]) =>
|
||||
([this.lastNavigation, this.thisNavigation] = events)
|
||||
);
|
||||
|
||||
this.navigationSuccessSub = this.router.events
|
||||
.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe((event: NavigationEnd) => this.lastNavigationSuccessId = event.id);
|
||||
this.navigationSuccessSub = this.router.events
|
||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||
.subscribe((event: NavigationEnd) => (this.lastNavigationSuccessId = event.id));
|
||||
}
|
||||
|
||||
async canActivate() {
|
||||
return !(
|
||||
this.thisNavigation?.navigationTrigger === "hashchange" &&
|
||||
this.lastNavigation.navigationTrigger === "popstate" &&
|
||||
this.lastNavigationSuccessId === this.lastNavigation.id &&
|
||||
this.lastNavigation.url === this.thisNavigation?.url
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.navigationStartSub != null) {
|
||||
this.navigationStartSub.unsubscribe();
|
||||
}
|
||||
|
||||
async canActivate() {
|
||||
return !(this.thisNavigation?.navigationTrigger === 'hashchange' &&
|
||||
this.lastNavigation.navigationTrigger === 'popstate' &&
|
||||
this.lastNavigationSuccessId === this.lastNavigation.id &&
|
||||
this.lastNavigation.url === this.thisNavigation?.url);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.navigationStartSub != null) {
|
||||
this.navigationStartSub.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.navigationSuccessSub != null) {
|
||||
this.navigationSuccessSub.unsubscribe();
|
||||
}
|
||||
if (this.navigationSuccessSub != null) {
|
||||
this.navigationSuccessSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
CanActivate,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { Injectable } from "@angular/core";
|
||||
import { CanActivate, Router } from "@angular/router";
|
||||
|
||||
import { UnauthGuardService } from 'jslib-angular/services/unauth-guard.service';
|
||||
import { UnauthGuardService } from "jslib-angular/services/unauth-guard.service";
|
||||
|
||||
@Injectable()
|
||||
export class LaunchGuardService implements CanActivate {
|
||||
constructor(private router: Router, private unauthGuardService: UnauthGuardService) { }
|
||||
constructor(private router: Router, private unauthGuardService: UnauthGuardService) {}
|
||||
|
||||
async canActivate() {
|
||||
if (BrowserApi.getBackgroundPage() == null) {
|
||||
this.router.navigate(['private-mode']);
|
||||
return false;
|
||||
}
|
||||
return await this.unauthGuardService.canActivate();
|
||||
async canActivate() {
|
||||
if (BrowserApi.getBackgroundPage() == null) {
|
||||
this.router.navigate(["private-mode"]);
|
||||
return false;
|
||||
}
|
||||
return await this.unauthGuardService.canActivate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { LockGuardService as BaseLockGuardService } from 'jslib-angular/services/lock-guard.service';
|
||||
import { LockGuardService as BaseLockGuardService } from "jslib-angular/services/lock-guard.service";
|
||||
|
||||
@Injectable()
|
||||
export class LockGuardService extends BaseLockGuardService {
|
||||
protected homepage = 'tabs/current';
|
||||
protected homepage = "tabs/current";
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PasswordRepromptService as BasePasswordRepromptService } from "jslib-angular/services/passwordReprompt.service";
|
||||
|
||||
import { PasswordRepromptService as BasePasswordRepromptService } from 'jslib-angular/services/passwordReprompt.service';
|
||||
|
||||
import { PasswordRepromptComponent } from '../components/password-reprompt.component';
|
||||
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
||||
|
||||
@Injectable()
|
||||
export class PasswordRepromptService extends BasePasswordRepromptService {
|
||||
component = PasswordRepromptComponent;
|
||||
component = PasswordRepromptComponent;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
import { ConsoleLogService } from 'jslib-common/services/consoleLog.service';
|
||||
import { SearchService } from 'jslib-common/services/search.service';
|
||||
import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
|
||||
import { SearchService } from "jslib-common/services/search.service";
|
||||
|
||||
export class PopupSearchService extends SearchService {
|
||||
constructor(private mainSearchService: SearchService, cipherService: CipherService,
|
||||
consoleLogService: ConsoleLogService, i18nService: I18nService) {
|
||||
super(cipherService, consoleLogService, i18nService);
|
||||
}
|
||||
constructor(
|
||||
private mainSearchService: SearchService,
|
||||
cipherService: CipherService,
|
||||
consoleLogService: ConsoleLogService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
super(cipherService, consoleLogService, i18nService);
|
||||
}
|
||||
|
||||
clearIndex() {
|
||||
throw new Error('Not available.');
|
||||
}
|
||||
clearIndex() {
|
||||
throw new Error("Not available.");
|
||||
}
|
||||
|
||||
indexCiphers(): Promise<void> {
|
||||
throw new Error('Not available.');
|
||||
}
|
||||
indexCiphers(): Promise<void> {
|
||||
throw new Error("Not available.");
|
||||
}
|
||||
|
||||
getIndexForSearch() {
|
||||
return this.mainSearchService.getIndexForSearch();
|
||||
}
|
||||
getIndexForSearch() {
|
||||
return this.mainSearchService.getIndexForSearch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +1,81 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
@Injectable()
|
||||
export class PopupUtilsService {
|
||||
constructor(private platformUtilsService: PlatformUtilsService) { }
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
|
||||
inSidebar(win: Window): boolean {
|
||||
return win.location.search !== '' && win.location.search.indexOf('uilocation=sidebar') > -1;
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
getContentScrollY(win: Window, scrollingContainer: string = "content"): number {
|
||||
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
||||
return content.scrollTop;
|
||||
}
|
||||
|
||||
setContentScrollY(win: Window, scrollY: number, scrollingContainer: string = "content"): void {
|
||||
if (scrollY != null) {
|
||||
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
||||
content.scrollTop = scrollY;
|
||||
}
|
||||
}
|
||||
|
||||
popOut(win: Window, href: string = null): void {
|
||||
if (href === null) {
|
||||
href = win.location.href;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
getContentScrollY(win: Window, scrollingContainer: string = 'content'): number {
|
||||
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
||||
return content.scrollTop;
|
||||
}
|
||||
|
||||
setContentScrollY(win: Window, scrollY: number, scrollingContainer: string = 'content'): void {
|
||||
if (scrollY != null) {
|
||||
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
||||
content.scrollTop = scrollY;
|
||||
}
|
||||
}
|
||||
|
||||
popOut(win: Window, href: string = null): void {
|
||||
|
||||
if (href === null) {
|
||||
href = win.location.href;
|
||||
}
|
||||
|
||||
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: Math.round(bodyRect.width ? bodyRect.width + 60 : 375),
|
||||
height: Math.round(bodyRect.height || 600),
|
||||
});
|
||||
|
||||
if (this.inPopup(win)) {
|
||||
BrowserApi.closePopup(win);
|
||||
}
|
||||
} 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,
|
||||
});
|
||||
}
|
||||
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: Math.round(bodyRect.width ? bodyRect.width + 60 : 375),
|
||||
height: Math.round(bodyRect.height || 600),
|
||||
});
|
||||
|
||||
if (this.inPopup(win)) {
|
||||
BrowserApi.closePopup(win);
|
||||
}
|
||||
} 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,225 +1,276 @@
|
||||
import {
|
||||
APP_INITIALIZER,
|
||||
LOCALE_ID,
|
||||
NgModule,
|
||||
} from '@angular/core';
|
||||
import { APP_INITIALIZER, LOCALE_ID, NgModule } from "@angular/core";
|
||||
|
||||
import { DebounceNavigationService } from './debounceNavigationService';
|
||||
import { LaunchGuardService } from './launch-guard.service';
|
||||
import { LockGuardService } from './lock-guard.service';
|
||||
import { PasswordRepromptService } from './password-reprompt.service';
|
||||
import { UnauthGuardService } from './unauth-guard.service';
|
||||
import { DebounceNavigationService } from "./debounceNavigationService";
|
||||
import { LaunchGuardService } from "./launch-guard.service";
|
||||
import { LockGuardService } from "./lock-guard.service";
|
||||
import { PasswordRepromptService } from "./password-reprompt.service";
|
||||
import { UnauthGuardService } from "./unauth-guard.service";
|
||||
|
||||
import { JslibServicesModule } from 'jslib-angular/services/jslib-services.module';
|
||||
import { LockGuardService as BaseLockGuardService } from 'jslib-angular/services/lock-guard.service';
|
||||
import { UnauthGuardService as BaseUnauthGuardService } from 'jslib-angular/services/unauth-guard.service';
|
||||
import { JslibServicesModule } from "jslib-angular/services/jslib-services.module";
|
||||
import { LockGuardService as BaseLockGuardService } from "jslib-angular/services/lock-guard.service";
|
||||
import { UnauthGuardService as BaseUnauthGuardService } from "jslib-angular/services/unauth-guard.service";
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AppIdService } from 'jslib-common/abstractions/appId.service';
|
||||
import { AuditService } from 'jslib-common/abstractions/audit.service';
|
||||
import { AuthService as AuthServiceAbstraction } from 'jslib-common/abstractions/auth.service';
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { ExportService } from 'jslib-common/abstractions/export.service';
|
||||
import { FileUploadService } from 'jslib-common/abstractions/fileUpload.service';
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { LogService as LogServiceAbstraction } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { NotificationsService } from 'jslib-common/abstractions/notifications.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SearchService as SearchServiceAbstraction } from 'jslib-common/abstractions/search.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { SettingsService } from 'jslib-common/abstractions/settings.service';
|
||||
import { StateService as StateServiceAbstraction } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { TokenService } from 'jslib-common/abstractions/token.service';
|
||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AppIdService } from "jslib-common/abstractions/appId.service";
|
||||
import { AuditService } from "jslib-common/abstractions/audit.service";
|
||||
import { AuthService as AuthServiceAbstraction } from "jslib-common/abstractions/auth.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { ExportService } from "jslib-common/abstractions/export.service";
|
||||
import { FileUploadService } from "jslib-common/abstractions/fileUpload.service";
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { NotificationsService } from "jslib-common/abstractions/notifications.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SearchService as SearchServiceAbstraction } from "jslib-common/abstractions/search.service";
|
||||
import { SendService } from "jslib-common/abstractions/send.service";
|
||||
import { SettingsService } from "jslib-common/abstractions/settings.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { AutofillService } from '../../services/abstractions/autofill.service';
|
||||
import BrowserMessagingService from '../../services/browserMessaging.service';
|
||||
import { AutofillService } from "../../services/abstractions/autofill.service";
|
||||
import BrowserMessagingService from "../../services/browserMessaging.service";
|
||||
|
||||
import { AuthService } from 'jslib-common/services/auth.service';
|
||||
import { ConsoleLogService } from 'jslib-common/services/consoleLog.service';
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { SearchService } from 'jslib-common/services/search.service';
|
||||
import { StateService } from 'jslib-common/services/state.service';
|
||||
import { AuthService } from "jslib-common/services/auth.service";
|
||||
import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
import { SearchService } from "jslib-common/services/search.service";
|
||||
import { StateService } from "jslib-common/services/state.service";
|
||||
|
||||
import { PopupSearchService } from './popup-search.service';
|
||||
import { PopupUtilsService } from './popup-utils.service';
|
||||
import { PopupSearchService } from "./popup-search.service";
|
||||
import { PopupUtilsService } from "./popup-utils.service";
|
||||
|
||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
|
||||
function getBgService<T>(service: string) {
|
||||
return (): T => {
|
||||
const page = BrowserApi.getBackgroundPage();
|
||||
return page ? page.bitwardenMain[service] as T : null;
|
||||
};
|
||||
return (): T => {
|
||||
const page = BrowserApi.getBackgroundPage();
|
||||
return page ? (page.bitwardenMain[service] as T) : null;
|
||||
};
|
||||
}
|
||||
|
||||
const isPrivateMode = BrowserApi.getBackgroundPage() == null;
|
||||
|
||||
export function initFactory(platformUtilsService: PlatformUtilsService, i18nService: I18nService,
|
||||
storageService: StorageService, popupUtilsService: PopupUtilsService, stateService: StateServiceAbstraction,
|
||||
logService: LogServiceAbstraction): Function {
|
||||
return async () => {
|
||||
if (!popupUtilsService.inPopup(window)) {
|
||||
window.document.body.classList.add('body-full');
|
||||
} else if (window.screen.availHeight < 600) {
|
||||
window.document.body.classList.add('body-xs');
|
||||
} else if (window.screen.availHeight <= 800) {
|
||||
window.document.body.classList.add('body-sm');
|
||||
export function initFactory(
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
storageService: StorageService,
|
||||
popupUtilsService: PopupUtilsService,
|
||||
stateService: StateServiceAbstraction,
|
||||
logService: LogServiceAbstraction
|
||||
): Function {
|
||||
return async () => {
|
||||
if (!popupUtilsService.inPopup(window)) {
|
||||
window.document.body.classList.add("body-full");
|
||||
} else if (window.screen.availHeight < 600) {
|
||||
window.document.body.classList.add("body-xs");
|
||||
} else if (window.screen.availHeight <= 800) {
|
||||
window.document.body.classList.add("body-sm");
|
||||
}
|
||||
|
||||
if (!isPrivateMode) {
|
||||
await stateService.save(
|
||||
ConstantsService.disableFaviconKey,
|
||||
await storageService.get<boolean>(ConstantsService.disableFaviconKey)
|
||||
);
|
||||
|
||||
await stateService.save(
|
||||
ConstantsService.disableBadgeCounterKey,
|
||||
await storageService.get<boolean>(ConstantsService.disableBadgeCounterKey)
|
||||
);
|
||||
|
||||
const htmlEl = window.document.documentElement;
|
||||
const theme = await platformUtilsService.getEffectiveTheme();
|
||||
htmlEl.classList.add("theme_" + theme);
|
||||
platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||
const bwTheme = await storageService.get<ThemeType>(ConstantsService.themeKey);
|
||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + sysTheme);
|
||||
}
|
||||
});
|
||||
htmlEl.classList.add("locale_" + i18nService.translationLocale);
|
||||
|
||||
if (!isPrivateMode) {
|
||||
await stateService.save(ConstantsService.disableFaviconKey,
|
||||
await storageService.get<boolean>(ConstantsService.disableFaviconKey));
|
||||
|
||||
await stateService.save(ConstantsService.disableBadgeCounterKey,
|
||||
await storageService.get<boolean>(ConstantsService.disableBadgeCounterKey));
|
||||
|
||||
const htmlEl = window.document.documentElement;
|
||||
const theme = await platformUtilsService.getEffectiveTheme();
|
||||
htmlEl.classList.add('theme_' + theme);
|
||||
platformUtilsService.onDefaultSystemThemeChange(async sysTheme => {
|
||||
const bwTheme = await storageService.get<ThemeType>(ConstantsService.themeKey);
|
||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove('theme_' + ThemeType.Light, 'theme_' + ThemeType.Dark);
|
||||
htmlEl.classList.add('theme_' + sysTheme);
|
||||
}
|
||||
});
|
||||
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
||||
|
||||
// Workaround for slow performance on external monitors on Chrome + MacOS
|
||||
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
|
||||
if (platformUtilsService.isChrome() &&
|
||||
navigator.platform.indexOf('Mac') > -1 &&
|
||||
popupUtilsService.inPopup(window) &&
|
||||
(window.screenLeft < 0 ||
|
||||
window.screenTop < 0 ||
|
||||
window.screenLeft > window.screen.width ||
|
||||
window.screenTop > window.screen.height)) {
|
||||
htmlEl.classList.add('force_redraw');
|
||||
logService.info('Force redraw is on');
|
||||
}
|
||||
}
|
||||
};
|
||||
// Workaround for slow performance on external monitors on Chrome + MacOS
|
||||
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
|
||||
if (
|
||||
platformUtilsService.isChrome() &&
|
||||
navigator.platform.indexOf("Mac") > -1 &&
|
||||
popupUtilsService.inPopup(window) &&
|
||||
(window.screenLeft < 0 ||
|
||||
window.screenTop < 0 ||
|
||||
window.screenLeft > window.screen.width ||
|
||||
window.screenTop > window.screen.height)
|
||||
) {
|
||||
htmlEl.classList.add("force_redraw");
|
||||
logService.info("Force redraw is on");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
JslibServicesModule,
|
||||
],
|
||||
declarations: [],
|
||||
providers: [
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useFactory: () => isPrivateMode ? null : getBgService<I18nService>('i18nService')().translationLocale,
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [
|
||||
PlatformUtilsService,
|
||||
I18nService,
|
||||
StorageService,
|
||||
PopupUtilsService,
|
||||
StateServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
],
|
||||
multi: true,
|
||||
},
|
||||
LaunchGuardService,
|
||||
{ provide: BaseLockGuardService, useClass: LockGuardService },
|
||||
{ provide: BaseUnauthGuardService, useClass: UnauthGuardService },
|
||||
DebounceNavigationService,
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useFactory: () =>
|
||||
isPrivateMode ? null : getBgService<I18nService>("i18nService")().translationLocale,
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [
|
||||
PlatformUtilsService,
|
||||
I18nService,
|
||||
StorageService,
|
||||
PopupUtilsService,
|
||||
{ provide: MessagingService, useClass: BrowserMessagingService },
|
||||
{ provide: AuthServiceAbstraction, useFactory: getBgService<AuthService>('authService'), deps: [] },
|
||||
{ provide: StateServiceAbstraction, useClass: StateService },
|
||||
{
|
||||
provide: SearchServiceAbstraction,
|
||||
useFactory: (cipherService: CipherService, logService: ConsoleLogService, i18nService: I18nService) => {
|
||||
return isPrivateMode ? null : new PopupSearchService(getBgService<SearchService>('searchService')(),
|
||||
cipherService, logService, i18nService);
|
||||
},
|
||||
deps: [
|
||||
CipherService,
|
||||
LogServiceAbstraction,
|
||||
I18nService,
|
||||
],
|
||||
},
|
||||
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },
|
||||
{ provide: FileUploadService, useFactory: getBgService<FileUploadService>('fileUploadService'), deps: [] },
|
||||
{ provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] },
|
||||
{
|
||||
provide: CryptoFunctionService,
|
||||
useFactory: getBgService<CryptoFunctionService>('cryptoFunctionService'),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] },
|
||||
{ provide: CollectionService, useFactory: getBgService<CollectionService>('collectionService'), deps: [] },
|
||||
{ provide: LogServiceAbstraction, useFactory: getBgService<ConsoleLogService>('logService'), deps: [] },
|
||||
{ provide: EnvironmentService, useFactory: getBgService<EnvironmentService>('environmentService'), deps: [] },
|
||||
{ provide: TotpService, useFactory: getBgService<TotpService>('totpService'), deps: [] },
|
||||
{ provide: TokenService, useFactory: getBgService<TokenService>('tokenService'), deps: [] },
|
||||
{ provide: I18nService, useFactory: getBgService<I18nService>('i18nService'), deps: [] },
|
||||
{ provide: CryptoService, useFactory: getBgService<CryptoService>('cryptoService'), deps: [] },
|
||||
{ provide: EventService, useFactory: getBgService<EventService>('eventService'), deps: [] },
|
||||
{ provide: PolicyService, useFactory: getBgService<PolicyService>('policyService'), deps: [] },
|
||||
{
|
||||
provide: PlatformUtilsService,
|
||||
useFactory: getBgService<PlatformUtilsService>('platformUtilsService'),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: PasswordGenerationService,
|
||||
useFactory: getBgService<PasswordGenerationService>('passwordGenerationService'),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: ApiService, useFactory: getBgService<ApiService>('apiService'), deps: [] },
|
||||
{ provide: SyncService, useFactory: getBgService<SyncService>('syncService'), deps: [] },
|
||||
{ provide: UserService, useFactory: getBgService<UserService>('userService'), deps: [] },
|
||||
{ provide: SettingsService, useFactory: getBgService<SettingsService>('settingsService'), deps: [] },
|
||||
{ provide: StorageService, useFactory: getBgService<StorageService>('storageService'), deps: [] },
|
||||
{ provide: AppIdService, useFactory: getBgService<AppIdService>('appIdService'), deps: [] },
|
||||
{ provide: AutofillService, useFactory: getBgService<AutofillService>('autofillService'), deps: [] },
|
||||
{ provide: ExportService, useFactory: getBgService<ExportService>('exportService'), deps: [] },
|
||||
{ provide: SendService, useFactory: getBgService<SendService>('sendService'), deps: [] },
|
||||
{ provide: KeyConnectorService, useFactory: getBgService<KeyConnectorService>('keyConnectorService'), deps: [] },
|
||||
{
|
||||
provide: UserVerificationService,
|
||||
useFactory: getBgService<UserVerificationService>('userVerificationService'),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: VaultTimeoutService,
|
||||
useFactory: getBgService<VaultTimeoutService>('vaultTimeoutService'),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: NotificationsService,
|
||||
useFactory: getBgService<NotificationsService>('notificationsService'),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: LogServiceAbstraction, useFactory: getBgService<ConsoleLogService>('logService'), deps: [] },
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
],
|
||||
StateServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
],
|
||||
multi: true,
|
||||
},
|
||||
LaunchGuardService,
|
||||
{ provide: BaseLockGuardService, useClass: LockGuardService },
|
||||
{ provide: BaseUnauthGuardService, useClass: UnauthGuardService },
|
||||
DebounceNavigationService,
|
||||
PopupUtilsService,
|
||||
{ provide: MessagingService, useClass: BrowserMessagingService },
|
||||
{
|
||||
provide: AuthServiceAbstraction,
|
||||
useFactory: getBgService<AuthService>("authService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: StateServiceAbstraction, useClass: StateService },
|
||||
{
|
||||
provide: SearchServiceAbstraction,
|
||||
useFactory: (
|
||||
cipherService: CipherService,
|
||||
logService: ConsoleLogService,
|
||||
i18nService: I18nService
|
||||
) => {
|
||||
return isPrivateMode
|
||||
? null
|
||||
: new PopupSearchService(
|
||||
getBgService<SearchService>("searchService")(),
|
||||
cipherService,
|
||||
logService,
|
||||
i18nService
|
||||
);
|
||||
},
|
||||
deps: [CipherService, LogServiceAbstraction, I18nService],
|
||||
},
|
||||
{ provide: AuditService, useFactory: getBgService<AuditService>("auditService"), deps: [] },
|
||||
{
|
||||
provide: FileUploadService,
|
||||
useFactory: getBgService<FileUploadService>("fileUploadService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: CipherService, useFactory: getBgService<CipherService>("cipherService"), deps: [] },
|
||||
{
|
||||
provide: CryptoFunctionService,
|
||||
useFactory: getBgService<CryptoFunctionService>("cryptoFunctionService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: FolderService, useFactory: getBgService<FolderService>("folderService"), deps: [] },
|
||||
{
|
||||
provide: CollectionService,
|
||||
useFactory: getBgService<CollectionService>("collectionService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: LogServiceAbstraction,
|
||||
useFactory: getBgService<ConsoleLogService>("logService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: EnvironmentService,
|
||||
useFactory: getBgService<EnvironmentService>("environmentService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: TotpService, useFactory: getBgService<TotpService>("totpService"), deps: [] },
|
||||
{ provide: TokenService, useFactory: getBgService<TokenService>("tokenService"), deps: [] },
|
||||
{ provide: I18nService, useFactory: getBgService<I18nService>("i18nService"), deps: [] },
|
||||
{ provide: CryptoService, useFactory: getBgService<CryptoService>("cryptoService"), deps: [] },
|
||||
{ provide: EventService, useFactory: getBgService<EventService>("eventService"), deps: [] },
|
||||
{ provide: PolicyService, useFactory: getBgService<PolicyService>("policyService"), deps: [] },
|
||||
{
|
||||
provide: PlatformUtilsService,
|
||||
useFactory: getBgService<PlatformUtilsService>("platformUtilsService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: PasswordGenerationService,
|
||||
useFactory: getBgService<PasswordGenerationService>("passwordGenerationService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: ApiService, useFactory: getBgService<ApiService>("apiService"), deps: [] },
|
||||
{ provide: SyncService, useFactory: getBgService<SyncService>("syncService"), deps: [] },
|
||||
{ provide: UserService, useFactory: getBgService<UserService>("userService"), deps: [] },
|
||||
{
|
||||
provide: SettingsService,
|
||||
useFactory: getBgService<SettingsService>("settingsService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: StorageService,
|
||||
useFactory: getBgService<StorageService>("storageService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: AppIdService, useFactory: getBgService<AppIdService>("appIdService"), deps: [] },
|
||||
{
|
||||
provide: AutofillService,
|
||||
useFactory: getBgService<AutofillService>("autofillService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: ExportService, useFactory: getBgService<ExportService>("exportService"), deps: [] },
|
||||
{ provide: SendService, useFactory: getBgService<SendService>("sendService"), deps: [] },
|
||||
{
|
||||
provide: KeyConnectorService,
|
||||
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: UserVerificationService,
|
||||
useFactory: getBgService<UserVerificationService>("userVerificationService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: VaultTimeoutService,
|
||||
useFactory: getBgService<VaultTimeoutService>("vaultTimeoutService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: NotificationsService,
|
||||
useFactory: getBgService<NotificationsService>("notificationsService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: LogServiceAbstraction,
|
||||
useFactory: getBgService<ConsoleLogService>("logService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
],
|
||||
})
|
||||
export class ServicesModule {
|
||||
}
|
||||
export class ServicesModule {}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { UnauthGuardService as BaseUnauthGuardService } from 'jslib-angular/services/unauth-guard.service';
|
||||
import { UnauthGuardService as BaseUnauthGuardService } from "jslib-angular/services/unauth-guard.service";
|
||||
|
||||
@Injectable()
|
||||
export class UnauthGuardService extends BaseUnauthGuardService {
|
||||
protected homepage = 'tabs/current';
|
||||
protected homepage = "tabs/current";
|
||||
}
|
||||
|
||||
@@ -1,53 +1,85 @@
|
||||
<form #form (ngSubmit)="submit()">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'excludedDomains' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick>{{'save' | i18n}}</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="excludedDomains">
|
||||
<div class="box-content-row box-content-row-multi" appBoxRow
|
||||
*ngFor="let domain of excludedDomains; let i = index; trackBy:trackByFunction">
|
||||
<button type="button" appStopClick (click)="removeUri(i)" appA11yTitle="{{'remove' | i18n}}">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="row-main">
|
||||
<label for="excludedDomain{{i}}">{{'uriPosition' | i18n : (i + 1)}}</label>
|
||||
<input id="excludedDomain{{i}}" name="excludedDomain{{i}}" type="text" [(ngModel)]="domain.uri"
|
||||
placeholder="{{'ex' | i18n}} https://google.com" inputmode="url" appInputVerbatim>
|
||||
<label for="currentUris{{i}}" class="sr-only">
|
||||
{{'currentUri' | i18n}} {{(i + 1)}}
|
||||
</label>
|
||||
<select *ngIf="currentUris && currentUris.length" id="currentUris{{i}}"
|
||||
name="currentUris{{i}}" [(ngModel)]="domain.uri" [hidden]="!domain.showCurrentUris">
|
||||
<option [ngValue]="null">-- {{'select' | i18n}} --</option>
|
||||
<option *ngFor="let u of currentUris" [ngValue]="u">{{u}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" *ngIf="currentUris && currentUris.length" class="row-btn" appStopClick
|
||||
appBlurClick appA11yTitle="{{'toggleCurrentUris' | i18n}}" (click)="toggleUriInput(domain)">
|
||||
<i aria-hidden="true" class="fa fa-lg fa-list"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<button type="button" appStopClick appBlurClick (click)="addUri()"
|
||||
class="box-content-row box-content-row-newmulti">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{'newUri' | i18n}}
|
||||
</button>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "excludedDomains" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick>{{ "save" | i18n }}</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="excludedDomains">
|
||||
<div
|
||||
class="box-content-row box-content-row-multi"
|
||||
appBoxRow
|
||||
*ngFor="let domain of excludedDomains; let i = index; trackBy: trackByFunction"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="removeUri(i)"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="row-main">
|
||||
<label for="excludedDomain{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
||||
<input
|
||||
id="excludedDomain{{ i }}"
|
||||
name="excludedDomain{{ i }}"
|
||||
type="text"
|
||||
[(ngModel)]="domain.uri"
|
||||
placeholder="{{ 'ex' | i18n }} https://google.com"
|
||||
inputmode="url"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<label for="currentUris{{ i }}" class="sr-only">
|
||||
{{ "currentUri" | i18n }} {{ i + 1 }}
|
||||
</label>
|
||||
<select
|
||||
*ngIf="currentUris && currentUris.length"
|
||||
id="currentUris{{ i }}"
|
||||
name="currentUris{{ i }}"
|
||||
[(ngModel)]="domain.uri"
|
||||
[hidden]="!domain.showCurrentUris"
|
||||
>
|
||||
<option [ngValue]="null">-- {{ "select" | i18n }} --</option>
|
||||
<option *ngFor="let u of currentUris" [ngValue]="u">{{ u }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'excludedDomainsDesc' | i18n}}
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
*ngIf="currentUris && currentUris.length"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleCurrentUris' | i18n }}"
|
||||
(click)="toggleUriInput(domain)"
|
||||
>
|
||||
<i aria-hidden="true" class="fa fa-lg fa-list"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
</div>
|
||||
</ng-container>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="addUri()"
|
||||
class="box-content-row box-content-row-newmulti"
|
||||
>
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{ "newUri" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "excludedDomainsDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,115 +1,119 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
|
||||
import { Router } from '@angular/router';
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
interface ExcludedDomain {
|
||||
uri: string;
|
||||
showCurrentUris: boolean;
|
||||
uri: string;
|
||||
showCurrentUris: boolean;
|
||||
}
|
||||
|
||||
const BroadcasterSubscriptionId = 'excludedDomains';
|
||||
const BroadcasterSubscriptionId = "excludedDomains";
|
||||
|
||||
@Component({
|
||||
selector: 'app-excluded-domains',
|
||||
templateUrl: 'excluded-domains.component.html',
|
||||
selector: "app-excluded-domains",
|
||||
templateUrl: "excluded-domains.component.html",
|
||||
})
|
||||
export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
||||
excludedDomains: ExcludedDomain[] = [];
|
||||
currentUris: string[];
|
||||
loadCurrentUrisTimeout: number;
|
||||
excludedDomains: ExcludedDomain[] = [];
|
||||
currentUris: string[];
|
||||
loadCurrentUrisTimeout: number;
|
||||
|
||||
constructor(private storageService: StorageService,
|
||||
private i18nService: I18nService, private router: Router,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
||||
private platformUtilsService: PlatformUtilsService) {
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private i18nService: I18nService,
|
||||
private router: Router,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
const savedDomains = await this.storageService.get<any>(ConstantsService.neverDomainsKey);
|
||||
if (savedDomains) {
|
||||
for (const uri of Object.keys(savedDomains)) {
|
||||
this.excludedDomains.push({ uri: uri, showCurrentUris: false });
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
const savedDomains = await this.storageService.get<any>(ConstantsService.neverDomainsKey);
|
||||
if (savedDomains) {
|
||||
for (const uri of Object.keys(savedDomains)) {
|
||||
this.excludedDomains.push({ uri: uri, showCurrentUris: false });
|
||||
await this.loadCurrentUris();
|
||||
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
switch (message.command) {
|
||||
case "tabChanged":
|
||||
case "windowChanged":
|
||||
if (this.loadCurrentUrisTimeout != null) {
|
||||
window.clearTimeout(this.loadCurrentUrisTimeout);
|
||||
}
|
||||
this.loadCurrentUrisTimeout = window.setTimeout(
|
||||
async () => await this.loadCurrentUris(),
|
||||
500
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
await this.loadCurrentUris();
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
switch (message.command) {
|
||||
case 'tabChanged':
|
||||
case 'windowChanged':
|
||||
if (this.loadCurrentUrisTimeout != null) {
|
||||
window.clearTimeout(this.loadCurrentUrisTimeout);
|
||||
}
|
||||
this.loadCurrentUrisTimeout = window.setTimeout(async () => await this.loadCurrentUris(), 500);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
async addUri() {
|
||||
this.excludedDomains.push({ uri: "", showCurrentUris: false });
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
async removeUri(i: number) {
|
||||
this.excludedDomains.splice(i, 1);
|
||||
}
|
||||
|
||||
async addUri() {
|
||||
this.excludedDomains.push({ uri: '', showCurrentUris: false });
|
||||
}
|
||||
|
||||
async removeUri(i: number) {
|
||||
this.excludedDomains.splice(i, 1);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const savedDomains: { [name: string]: null } = {};
|
||||
for (const domain of this.excludedDomains) {
|
||||
if (domain.uri && domain.uri !== '') {
|
||||
const validDomain = Utils.getHostname(domain.uri);
|
||||
if (!validDomain) {
|
||||
this.platformUtilsService.showToast('error', null,
|
||||
this.i18nService.t('excludedDomainsInvalidDomain', domain.uri));
|
||||
return;
|
||||
}
|
||||
savedDomains[validDomain] = null;
|
||||
}
|
||||
async submit() {
|
||||
const savedDomains: { [name: string]: null } = {};
|
||||
for (const domain of this.excludedDomains) {
|
||||
if (domain.uri && domain.uri !== "") {
|
||||
const validDomain = Utils.getHostname(domain.uri);
|
||||
if (!validDomain) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("excludedDomainsInvalidDomain", domain.uri)
|
||||
);
|
||||
return;
|
||||
}
|
||||
await this.storageService.save(ConstantsService.neverDomainsKey, savedDomains);
|
||||
this.router.navigate(['/tabs/settings']);
|
||||
savedDomains[validDomain] = null;
|
||||
}
|
||||
}
|
||||
await this.storageService.save(ConstantsService.neverDomainsKey, savedDomains);
|
||||
this.router.navigate(["/tabs/settings"]);
|
||||
}
|
||||
|
||||
trackByFunction(index: number, item: any) {
|
||||
return index;
|
||||
}
|
||||
trackByFunction(index: number, item: any) {
|
||||
return index;
|
||||
}
|
||||
|
||||
toggleUriInput(domain: ExcludedDomain) {
|
||||
domain.showCurrentUris = !domain.showCurrentUris;
|
||||
}
|
||||
toggleUriInput(domain: ExcludedDomain) {
|
||||
domain.showCurrentUris = !domain.showCurrentUris;
|
||||
}
|
||||
|
||||
async loadCurrentUris() {
|
||||
const tabs = await BrowserApi.tabsQuery({ windowType: 'normal' });
|
||||
if (tabs) {
|
||||
const uriSet = new Set(tabs.map(tab => Utils.getHostname(tab.url)));
|
||||
uriSet.delete(null);
|
||||
this.currentUris = Array.from(uriSet);
|
||||
}
|
||||
async loadCurrentUris() {
|
||||
const tabs = await BrowserApi.tabsQuery({ windowType: "normal" });
|
||||
if (tabs) {
|
||||
const uriSet = new Set(tabs.map((tab) => Utils.getHostname(tab.url)));
|
||||
uriSet.delete(null);
|
||||
this.currentUris = Array.from(uriSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,39 @@
|
||||
<form (ngSubmit)="submit()" [formGroup]="exportForm">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon" aria-hidden="true"><i class="fa fa-chevron-left"></i></span>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'exportVault' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button appBlurClick type="submit" [disabled]="!exportForm.enabled">{{'submit' | i18n}}</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<app-callout type="warning" title="{{'vaultExportDisabled' | i18n}}" *ngIf="disabledByPolicy">
|
||||
{{'personalVaultExportPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon" aria-hidden="true"><i class="fa fa-chevron-left"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "exportVault" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button appBlurClick type="submit" [disabled]="!exportForm.enabled">
|
||||
{{ "submit" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<app-callout type="warning" title="{{ 'vaultExportDisabled' | i18n }}" *ngIf="disabledByPolicy">
|
||||
{{ "personalVaultExportPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="format">{{'fileFormat' | i18n}}</label>
|
||||
<select id="format" name="Format" formControlName="format">
|
||||
<option *ngFor="let f of formatOptions" [value]="f.value">{{f.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<app-verify-master-password ngDefaultControl formControlName="secret" name="Secret">
|
||||
</app-verify-master-password>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{'confirmIdentity' | i18n}}</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="format">{{ "fileFormat" | i18n }}</label>
|
||||
<select id="format" name="Format" formControlName="format">
|
||||
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</content>
|
||||
<app-verify-master-password ngDefaultControl formControlName="secret" name="Secret">
|
||||
</app-verify-master-password>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{ "confirmIdentity" | i18n }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,33 +1,51 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { ExportService } from 'jslib-common/abstractions/export.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { ExportService } from "jslib-common/abstractions/export.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
|
||||
import { ExportComponent as BaseExportComponent } from 'jslib-angular/components/export.component';
|
||||
import { ExportComponent as BaseExportComponent } from "jslib-angular/components/export.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-export',
|
||||
templateUrl: 'export.component.html',
|
||||
selector: "app-export",
|
||||
templateUrl: "export.component.html",
|
||||
})
|
||||
export class ExportComponent extends BaseExportComponent {
|
||||
constructor(cryptoService: CryptoService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, exportService: ExportService,
|
||||
eventService: EventService, policyService: PolicyService, private router: Router,
|
||||
logService: LogService, userVerificationService: UserVerificationService, fb: FormBuilder) {
|
||||
super(cryptoService, i18nService, platformUtilsService, exportService, eventService, policyService, window,
|
||||
logService, userVerificationService, fb);
|
||||
}
|
||||
constructor(
|
||||
cryptoService: CryptoService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
exportService: ExportService,
|
||||
eventService: EventService,
|
||||
policyService: PolicyService,
|
||||
private router: Router,
|
||||
logService: LogService,
|
||||
userVerificationService: UserVerificationService,
|
||||
fb: FormBuilder
|
||||
) {
|
||||
super(
|
||||
cryptoService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
exportService,
|
||||
eventService,
|
||||
policyService,
|
||||
window,
|
||||
logService,
|
||||
userVerificationService,
|
||||
fb
|
||||
);
|
||||
}
|
||||
|
||||
protected saved() {
|
||||
super.saved();
|
||||
this.router.navigate(['/tabs/settings']);
|
||||
}
|
||||
protected saved() {
|
||||
super.saved();
|
||||
this.router.navigate(["/tabs/settings"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,53 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/folders">{{'cancel' | i18n}}</a>
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/folders">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ title }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{ "save" | i18n }}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content *ngIf="folder">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{ "name" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="folder.name"
|
||||
[appAutofocus]="!editMode"
|
||||
/>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{title}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{'save' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content *ngIf="folder">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="folder.name" [appAutofocus]="!editMode">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list" *ngIf="editMode">
|
||||
<div class="box-content single-line">
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="delete()"
|
||||
[appApiAction]="deletePromise"
|
||||
#deleteBtn
|
||||
>
|
||||
<div class="row-main text-danger">
|
||||
<div class="icon text-danger" aria-hidden="true">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list" *ngIf="editMode">
|
||||
<div class="box-content single-line">
|
||||
<button type="button" class="box-content-row" appStopClick appBlurClick (click)="delete()"
|
||||
[appApiAction]="deletePromise" #deleteBtn>
|
||||
<div class="row-main text-danger">
|
||||
<div class="icon text-danger" aria-hidden="true">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</div>
|
||||
<span>{{'deleteFolder' | i18n}}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
<span>{{ "deleteFolder" | i18n }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from 'rxjs/operators';
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
FolderAddEditComponent as BaseFolderAddEditComponent,
|
||||
} from 'jslib-angular/components/folder-add-edit.component';
|
||||
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "jslib-angular/components/folder-add-edit.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-folder-add-edit',
|
||||
templateUrl: 'folder-add-edit.component.html',
|
||||
selector: "app-folder-add-edit",
|
||||
templateUrl: "folder-add-edit.component.html",
|
||||
})
|
||||
export class FolderAddEditComponent extends BaseFolderAddEditComponent {
|
||||
constructor(folderService: FolderService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, private router: Router,
|
||||
private route: ActivatedRoute, logService: LogService) {
|
||||
super(folderService, i18nService, platformUtilsService, logService);
|
||||
constructor(
|
||||
folderService: FolderService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
logService: LogService
|
||||
) {
|
||||
super(folderService, i18nService, platformUtilsService, logService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
if (params.folderId) {
|
||||
this.folderId = params.folderId;
|
||||
}
|
||||
await this.init();
|
||||
});
|
||||
}
|
||||
|
||||
async submit(): Promise<boolean> {
|
||||
if (await super.submit()) {
|
||||
this.router.navigate(["/folders"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.queryParams.pipe(first()).subscribe(async params => {
|
||||
if (params.folderId) {
|
||||
this.folderId = params.folderId;
|
||||
}
|
||||
await this.init();
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async submit(): Promise<boolean> {
|
||||
if (await super.submit()) {
|
||||
this.router.navigate(['/folders']);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async delete(): Promise<boolean> {
|
||||
const confirmed = await super.delete();
|
||||
if (confirmed) {
|
||||
this.router.navigate(['/folders']);
|
||||
}
|
||||
return confirmed;
|
||||
async delete(): Promise<boolean> {
|
||||
const confirmed = await super.delete();
|
||||
if (confirmed) {
|
||||
this.router.navigate(["/folders"]);
|
||||
}
|
||||
return confirmed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,39 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'folders' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button type="button" appBlurClick (click)="addFolder()" appA11yTitle="{{'addFolder' | i18n}}">
|
||||
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "folders" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button
|
||||
type="button"
|
||||
appBlurClick
|
||||
(click)="addFolder()"
|
||||
appA11yTitle="{{ 'addFolder' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box list full-list" *ngIf="folders && folders.length">
|
||||
<div class="box-content">
|
||||
<button type="button" appStopClick (click)="folderSelected(f)" class="box-content-row padded"
|
||||
*ngFor="let f of folders">{{f.name}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!folders || !folders.length">
|
||||
<p>{{'noFolders' | i18n}}</p>
|
||||
<div class="box list full-list" *ngIf="folders && folders.length">
|
||||
<div class="box-content">
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="folderSelected(f)"
|
||||
class="box-content-row padded"
|
||||
*ngFor="let f of folders"
|
||||
>
|
||||
{{ f.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!folders || !folders.length">
|
||||
<p>{{ "noFolders" | i18n }}</p>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { FolderView } from 'jslib-common/models/view/folderView';
|
||||
import { FolderView } from "jslib-common/models/view/folderView";
|
||||
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-folders',
|
||||
templateUrl: 'folders.component.html',
|
||||
selector: "app-folders",
|
||||
templateUrl: "folders.component.html",
|
||||
})
|
||||
export class FoldersComponent implements OnInit {
|
||||
folders: FolderView[];
|
||||
folders: FolderView[];
|
||||
|
||||
constructor(private folderService: FolderService, private router: Router) { }
|
||||
constructor(private folderService: FolderService, private router: Router) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.folders = await this.folderService.getAllDecrypted();
|
||||
// Remove "No Folder"
|
||||
if (this.folders.length > 0) {
|
||||
this.folders = this.folders.slice(0, this.folders.length - 1);
|
||||
}
|
||||
async ngOnInit() {
|
||||
this.folders = await this.folderService.getAllDecrypted();
|
||||
// Remove "No Folder"
|
||||
if (this.folders.length > 0) {
|
||||
this.folders = this.folders.slice(0, this.folders.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
folderSelected(folder: FolderView) {
|
||||
this.router.navigate(['/edit-folder'], { queryParams: { folderId: folder.id } });
|
||||
}
|
||||
folderSelected(folder: FolderView) {
|
||||
this.router.navigate(["/edit-folder"], { queryParams: { folderId: folder.id } });
|
||||
}
|
||||
|
||||
addFolder() {
|
||||
this.router.navigate(['/add-folder']);
|
||||
}
|
||||
addFolder() {
|
||||
this.router.navigate(["/add-folder"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,183 +1,258 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'options' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "options" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showGeneral = !showGeneral"
|
||||
[attr.aria-expanded]="showGeneral"
|
||||
>
|
||||
General
|
||||
<i *ngIf="!showGeneral" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showGeneral" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<ng-container *ngIf="showGeneral">
|
||||
<div class="box">
|
||||
<h2>
|
||||
<button type="button" class="box-header-expandable" (click)="showGeneral = !showGeneral" [attr.aria-expanded]="showGeneral">
|
||||
General
|
||||
<i *ngIf="!showGeneral" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showGeneral" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="defaultUriMatch">{{ "defaultUriMatchDetection" | i18n }}</label>
|
||||
<select
|
||||
id="defaultUriMatch"
|
||||
name="DefaultUriMatch"
|
||||
[(ngModel)]="defaultUriMatch"
|
||||
(change)="saveDefaultUriMatch()"
|
||||
>
|
||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "defaultUriMatchDetectionDesc" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngIf="showGeneral">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="defaultUriMatch">{{'defaultUriMatchDetection' | i18n}}</label>
|
||||
<select id="defaultUriMatch" name="DefaultUriMatch" [(ngModel)]="defaultUriMatch"
|
||||
(change)="saveDefaultUriMatch()">
|
||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'defaultUriMatchDetectionDesc' | i18n}}</div>
|
||||
<div class="box" *ngIf="showClearClipboard">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
||||
<select
|
||||
id="clearClipboard"
|
||||
name="ClearClipboard"
|
||||
[(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()"
|
||||
>
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box" *ngIf="showClearClipboard">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="clearClipboard">{{'clearClipboard' | i18n}}</label>
|
||||
<select id="clearClipboard" name="ClearClipboard" [(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()">
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'clearClipboardDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="totp">{{'disableAutoTotpCopy' | i18n}}</label>
|
||||
<input id="totp" type="checkbox" (change)="updateAutoTotpCopy()" [(ngModel)]="disableAutoTotpCopy">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'disableAutoTotpCopyDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="addlogin-notification-bar">{{'disableAddLoginNotification' | i18n}}</label>
|
||||
<input id="addlogin-notification-bar" type="checkbox" (change)="updateAddLoginNotification()"
|
||||
[(ngModel)]="disableAddLoginNotification">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'addLoginNotificationDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="changedpass-notification-bar">{{'disableChangedPasswordNotification' | i18n}}</label>
|
||||
<input id="changedpass-notification-bar" type="checkbox" (change)="updateChangedPasswordNotification()"
|
||||
[(ngModel)]="disableChangedPasswordNotification">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'disableChangedPasswordNotificationDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="context-menu">{{'disableContextMenuItem' | i18n}}</label>
|
||||
<input id="context-menu" type="checkbox" (change)="updateDisableContextMenuItem()"
|
||||
[(ngModel)]="disableContextMenuItem">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'disableContextMenuItemDesc' | i18n}}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box box-section-divider">
|
||||
<h2>
|
||||
<button type="button" class="box-header-expandable" (click)="showDisplay = !showDisplay" [attr.aria-expanded]="showDisplay">
|
||||
Display
|
||||
<i *ngIf="!showDisplay" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showDisplay" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="box-footer">{{ "clearClipboardDesc" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngIf="showDisplay">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="dontShowCards">{{'dontShowCardsCurrentTab' | i18n}}</label>
|
||||
<input id="dontShowCards" type="checkbox" (change)="updateShowCards()" [(ngModel)]="dontShowCards">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'dontShowCardsCurrentTabDesc' | i18n}}</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="totp">{{ "disableAutoTotpCopy" | i18n }}</label>
|
||||
<input
|
||||
id="totp"
|
||||
type="checkbox"
|
||||
(change)="updateAutoTotpCopy()"
|
||||
[(ngModel)]="disableAutoTotpCopy"
|
||||
/>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="dontShowIdentities">{{'dontShowIdentitiesCurrentTab' | i18n}}</label>
|
||||
<input id="dontShowIdentities" type="checkbox" (change)="updateShowIdentities()"
|
||||
[(ngModel)]="dontShowIdentities">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'dontShowIdentitiesCurrentTabDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="favicon">{{'disableFavicon' | i18n}}</label>
|
||||
<input id="favicon" type="checkbox" (change)="updateDisableFavicon()" [(ngModel)]="disableFavicon">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'disableFaviconDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="badge">{{'disableBadgeCounter' | i18n}}</label>
|
||||
<input id="badge" type="checkbox" (change)="updateDisableBadgeCounter()" [(ngModel)]="disableBadgeCounter">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'disableBadgeCounterDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="theme">{{'theme' | i18n}}</label>
|
||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'themeDesc' | i18n}}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box box-section-divider">
|
||||
<h2>
|
||||
<button type="button" class="box-header-expandable" (click)="showAutofill = !showAutofill" [attr.aria-expanded]="showAutofill">
|
||||
Autofill
|
||||
<i *ngIf="!showAutofill" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showAutofill" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="box-footer">{{ "disableAutoTotpCopyDesc" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngIf="showAutofill">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="autofill">{{'enableAutoFillOnPageLoad' | i18n}}</label>
|
||||
<input id="autofill" type="checkbox" (change)="updateAutoFillOnPageLoad()"
|
||||
[(ngModel)]="enableAutoFillOnPageLoad">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'enableAutoFillOnPageLoadDesc' | i18n}}
|
||||
<b>{{'warning' | i18n}}</b>: {{'experimentalFeature' | i18n}}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="addlogin-notification-bar">{{ "disableAddLoginNotification" | i18n }}</label>
|
||||
<input
|
||||
id="addlogin-notification-bar"
|
||||
type="checkbox"
|
||||
(change)="updateAddLoginNotification()"
|
||||
[(ngModel)]="disableAddLoginNotification"
|
||||
/>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="defaultAutofill">{{'defaultAutoFillOnPageLoad' | i18n}}</label>
|
||||
<select id="defaultAutofill" name="DefaultAutofill" [(ngModel)]="autoFillOnPageLoadDefault"
|
||||
(change)="updateAutoFillOnPageLoadDefault()" [disabled]="!enableAutoFillOnPageLoad">
|
||||
<option *ngFor="let o of autoFillOnPageLoadOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{'defaultAutoFillOnPageLoadDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "addLoginNotificationDesc" | i18n }}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="changedpass-notification-bar">{{
|
||||
"disableChangedPasswordNotification" | i18n
|
||||
}}</label>
|
||||
<input
|
||||
id="changedpass-notification-bar"
|
||||
type="checkbox"
|
||||
(change)="updateChangedPasswordNotification()"
|
||||
[(ngModel)]="disableChangedPasswordNotification"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="box-footer">{{ "disableChangedPasswordNotificationDesc" | i18n }}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="context-menu">{{ "disableContextMenuItem" | i18n }}</label>
|
||||
<input
|
||||
id="context-menu"
|
||||
type="checkbox"
|
||||
(change)="updateDisableContextMenuItem()"
|
||||
[(ngModel)]="disableContextMenuItem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "disableContextMenuItemDesc" | i18n }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box box-section-divider">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showDisplay = !showDisplay"
|
||||
[attr.aria-expanded]="showDisplay"
|
||||
>
|
||||
Display
|
||||
<i *ngIf="!showDisplay" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showDisplay" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<ng-container *ngIf="showDisplay">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="dontShowCards">{{ "dontShowCardsCurrentTab" | i18n }}</label>
|
||||
<input
|
||||
id="dontShowCards"
|
||||
type="checkbox"
|
||||
(change)="updateShowCards()"
|
||||
[(ngModel)]="dontShowCards"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "dontShowCardsCurrentTabDesc" | i18n }}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="dontShowIdentities">{{ "dontShowIdentitiesCurrentTab" | i18n }}</label>
|
||||
<input
|
||||
id="dontShowIdentities"
|
||||
type="checkbox"
|
||||
(change)="updateShowIdentities()"
|
||||
[(ngModel)]="dontShowIdentities"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "dontShowIdentitiesCurrentTabDesc" | i18n }}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="favicon">{{ "disableFavicon" | i18n }}</label>
|
||||
<input
|
||||
id="favicon"
|
||||
type="checkbox"
|
||||
(change)="updateDisableFavicon()"
|
||||
[(ngModel)]="disableFavicon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "disableFaviconDesc" | i18n }}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="badge">{{ "disableBadgeCounter" | i18n }}</label>
|
||||
<input
|
||||
id="badge"
|
||||
type="checkbox"
|
||||
(change)="updateDisableBadgeCounter()"
|
||||
[(ngModel)]="disableBadgeCounter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "disableBadgeCounterDesc" | i18n }}</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="theme">{{ "theme" | i18n }}</label>
|
||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "themeDesc" | i18n }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box box-section-divider">
|
||||
<h2>
|
||||
<button
|
||||
type="button"
|
||||
class="box-header-expandable"
|
||||
(click)="showAutofill = !showAutofill"
|
||||
[attr.aria-expanded]="showAutofill"
|
||||
>
|
||||
Autofill
|
||||
<i *ngIf="!showAutofill" class="fa fa-chevron-down fa-sm icon" aria-hidden="true"></i>
|
||||
<i *ngIf="showAutofill" class="fa fa-chevron-up fa-sm icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<ng-container *ngIf="showAutofill">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="autofill">{{ "enableAutoFillOnPageLoad" | i18n }}</label>
|
||||
<input
|
||||
id="autofill"
|
||||
type="checkbox"
|
||||
(change)="updateAutoFillOnPageLoad()"
|
||||
[(ngModel)]="enableAutoFillOnPageLoad"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "enableAutoFillOnPageLoadDesc" | i18n }}
|
||||
<b>{{ "warning" | i18n }}</b
|
||||
>: {{ "experimentalFeature" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="defaultAutofill">{{ "defaultAutoFillOnPageLoad" | i18n }}</label>
|
||||
<select
|
||||
id="defaultAutofill"
|
||||
name="DefaultAutofill"
|
||||
[(ngModel)]="autoFillOnPageLoadDefault"
|
||||
(change)="updateAutoFillOnPageLoadDefault()"
|
||||
[disabled]="!enableAutoFillOnPageLoad"
|
||||
>
|
||||
<option *ngFor="let o of autoFillOnPageLoadOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">{{ "defaultAutoFillOnPageLoadDesc" | i18n }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</content>
|
||||
|
||||
@@ -1,170 +1,211 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||
import { UriMatchType } from 'jslib-common/enums/uriMatchType';
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-options',
|
||||
templateUrl: 'options.component.html',
|
||||
selector: "app-options",
|
||||
templateUrl: "options.component.html",
|
||||
})
|
||||
export class OptionsComponent implements OnInit {
|
||||
disableFavicon = false;
|
||||
disableBadgeCounter = false;
|
||||
enableAutoFillOnPageLoad = false;
|
||||
autoFillOnPageLoadDefault = false;
|
||||
autoFillOnPageLoadOptions: any[];
|
||||
disableAutoTotpCopy = false;
|
||||
disableContextMenuItem = false;
|
||||
disableAddLoginNotification = false;
|
||||
disableChangedPasswordNotification = false;
|
||||
dontShowCards = false;
|
||||
dontShowIdentities = false;
|
||||
showClearClipboard = true;
|
||||
theme: string;
|
||||
themeOptions: any[];
|
||||
defaultUriMatch = UriMatchType.Domain;
|
||||
uriMatchOptions: any[];
|
||||
clearClipboard: number;
|
||||
clearClipboardOptions: any[];
|
||||
showGeneral: boolean = true;
|
||||
showAutofill: boolean = true;
|
||||
showDisplay: boolean = true;
|
||||
disableFavicon = false;
|
||||
disableBadgeCounter = false;
|
||||
enableAutoFillOnPageLoad = false;
|
||||
autoFillOnPageLoadDefault = false;
|
||||
autoFillOnPageLoadOptions: any[];
|
||||
disableAutoTotpCopy = false;
|
||||
disableContextMenuItem = false;
|
||||
disableAddLoginNotification = false;
|
||||
disableChangedPasswordNotification = false;
|
||||
dontShowCards = false;
|
||||
dontShowIdentities = false;
|
||||
showClearClipboard = true;
|
||||
theme: string;
|
||||
themeOptions: any[];
|
||||
defaultUriMatch = UriMatchType.Domain;
|
||||
uriMatchOptions: any[];
|
||||
clearClipboard: number;
|
||||
clearClipboardOptions: any[];
|
||||
showGeneral: boolean = true;
|
||||
showAutofill: boolean = true;
|
||||
showDisplay: boolean = true;
|
||||
|
||||
constructor(private messagingService: MessagingService, private storageService: StorageService,
|
||||
private stateService: StateService, private totpService: TotpService, i18nService: I18nService) {
|
||||
this.themeOptions = [
|
||||
{ name: i18nService.t('default'), value: null },
|
||||
{ name: i18nService.t('light'), value: ThemeType.Light },
|
||||
{ name: i18nService.t('dark'), value: ThemeType.Dark },
|
||||
{ name: 'Nord', value: ThemeType.Nord },
|
||||
{ name: i18nService.t('solarizedDark'), value: ThemeType.SolarizedDark },
|
||||
];
|
||||
this.uriMatchOptions = [
|
||||
{ name: i18nService.t('baseDomain'), value: UriMatchType.Domain },
|
||||
{ name: i18nService.t('host'), value: UriMatchType.Host },
|
||||
{ name: i18nService.t('startsWith'), value: UriMatchType.StartsWith },
|
||||
{ name: i18nService.t('regEx'), value: UriMatchType.RegularExpression },
|
||||
{ name: i18nService.t('exact'), value: UriMatchType.Exact },
|
||||
{ name: i18nService.t('never'), value: UriMatchType.Never },
|
||||
];
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t('never'), value: null },
|
||||
{ name: i18nService.t('tenSeconds'), value: 10 },
|
||||
{ name: i18nService.t('twentySeconds'), value: 20 },
|
||||
{ name: i18nService.t('thirtySeconds'), value: 30 },
|
||||
{ name: i18nService.t('oneMinute'), value: 60 },
|
||||
{ name: i18nService.t('twoMinutes'), value: 120 },
|
||||
{ name: i18nService.t('fiveMinutes'), value: 300 },
|
||||
];
|
||||
this.autoFillOnPageLoadOptions = [
|
||||
{ name: i18nService.t('autoFillOnPageLoadYes'), value: true },
|
||||
{ name: i18nService.t('autoFillOnPageLoadNo'), value: false },
|
||||
];
|
||||
}
|
||||
constructor(
|
||||
private messagingService: MessagingService,
|
||||
private storageService: StorageService,
|
||||
private stateService: StateService,
|
||||
private totpService: TotpService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
this.themeOptions = [
|
||||
{ name: i18nService.t("default"), value: null },
|
||||
{ name: i18nService.t("light"), value: ThemeType.Light },
|
||||
{ name: i18nService.t("dark"), value: ThemeType.Dark },
|
||||
{ name: "Nord", value: ThemeType.Nord },
|
||||
{ name: i18nService.t("solarizedDark"), value: ThemeType.SolarizedDark },
|
||||
];
|
||||
this.uriMatchOptions = [
|
||||
{ name: i18nService.t("baseDomain"), value: UriMatchType.Domain },
|
||||
{ name: i18nService.t("host"), value: UriMatchType.Host },
|
||||
{ name: i18nService.t("startsWith"), value: UriMatchType.StartsWith },
|
||||
{ name: i18nService.t("regEx"), value: UriMatchType.RegularExpression },
|
||||
{ name: i18nService.t("exact"), value: UriMatchType.Exact },
|
||||
{ name: i18nService.t("never"), value: UriMatchType.Never },
|
||||
];
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t("never"), value: null },
|
||||
{ name: i18nService.t("tenSeconds"), value: 10 },
|
||||
{ name: i18nService.t("twentySeconds"), value: 20 },
|
||||
{ name: i18nService.t("thirtySeconds"), value: 30 },
|
||||
{ name: i18nService.t("oneMinute"), value: 60 },
|
||||
{ name: i18nService.t("twoMinutes"), value: 120 },
|
||||
{ name: i18nService.t("fiveMinutes"), value: 300 },
|
||||
];
|
||||
this.autoFillOnPageLoadOptions = [
|
||||
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true },
|
||||
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
|
||||
];
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.enableAutoFillOnPageLoad = await this.storageService.get<boolean>(
|
||||
ConstantsService.enableAutoFillOnPageLoadKey);
|
||||
async ngOnInit() {
|
||||
this.enableAutoFillOnPageLoad = await this.storageService.get<boolean>(
|
||||
ConstantsService.enableAutoFillOnPageLoadKey
|
||||
);
|
||||
|
||||
this.autoFillOnPageLoadDefault = await this.storageService.get<boolean>(
|
||||
ConstantsService.autoFillOnPageLoadDefaultKey) ?? true;
|
||||
this.autoFillOnPageLoadDefault =
|
||||
(await this.storageService.get<boolean>(ConstantsService.autoFillOnPageLoadDefaultKey)) ??
|
||||
true;
|
||||
|
||||
this.disableAddLoginNotification = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableAddLoginNotificationKey);
|
||||
this.disableAddLoginNotification = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableAddLoginNotificationKey
|
||||
);
|
||||
|
||||
this.disableChangedPasswordNotification = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableChangedPasswordNotificationKey);
|
||||
this.disableChangedPasswordNotification = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableChangedPasswordNotificationKey
|
||||
);
|
||||
|
||||
this.disableContextMenuItem = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableContextMenuItemKey);
|
||||
this.disableContextMenuItem = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableContextMenuItemKey
|
||||
);
|
||||
|
||||
this.dontShowCards = await this.storageService.get<boolean>(ConstantsService.dontShowCardsCurrentTab);
|
||||
this.dontShowIdentities = await this.storageService.get<boolean>(ConstantsService.dontShowIdentitiesCurrentTab);
|
||||
this.dontShowCards = await this.storageService.get<boolean>(
|
||||
ConstantsService.dontShowCardsCurrentTab
|
||||
);
|
||||
this.dontShowIdentities = await this.storageService.get<boolean>(
|
||||
ConstantsService.dontShowIdentitiesCurrentTab
|
||||
);
|
||||
|
||||
this.disableAutoTotpCopy = !(await this.totpService.isAutoCopyEnabled());
|
||||
this.disableAutoTotpCopy = !(await this.totpService.isAutoCopyEnabled());
|
||||
|
||||
this.disableFavicon = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
this.disableFavicon = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableFaviconKey
|
||||
);
|
||||
|
||||
this.disableBadgeCounter = await this.storageService.get<boolean>(ConstantsService.disableBadgeCounterKey);
|
||||
this.disableBadgeCounter = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableBadgeCounterKey
|
||||
);
|
||||
|
||||
this.theme = await this.storageService.get<string>(ConstantsService.themeKey);
|
||||
this.theme = await this.storageService.get<string>(ConstantsService.themeKey);
|
||||
|
||||
const defaultUriMatch = await this.storageService.get<UriMatchType>(ConstantsService.defaultUriMatch);
|
||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
|
||||
const defaultUriMatch = await this.storageService.get<UriMatchType>(
|
||||
ConstantsService.defaultUriMatch
|
||||
);
|
||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
|
||||
|
||||
this.clearClipboard = await this.storageService.get<number>(ConstantsService.clearClipboardKey);
|
||||
}
|
||||
this.clearClipboard = await this.storageService.get<number>(ConstantsService.clearClipboardKey);
|
||||
}
|
||||
|
||||
async updateAddLoginNotification() {
|
||||
await this.storageService.save(ConstantsService.disableAddLoginNotificationKey,
|
||||
this.disableAddLoginNotification);
|
||||
}
|
||||
async updateAddLoginNotification() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.disableAddLoginNotificationKey,
|
||||
this.disableAddLoginNotification
|
||||
);
|
||||
}
|
||||
|
||||
async updateChangedPasswordNotification() {
|
||||
await this.storageService.save(ConstantsService.disableChangedPasswordNotificationKey,
|
||||
this.disableChangedPasswordNotification);
|
||||
}
|
||||
async updateChangedPasswordNotification() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.disableChangedPasswordNotificationKey,
|
||||
this.disableChangedPasswordNotification
|
||||
);
|
||||
}
|
||||
|
||||
async updateDisableContextMenuItem() {
|
||||
await this.storageService.save(ConstantsService.disableContextMenuItemKey,
|
||||
this.disableContextMenuItem);
|
||||
this.messagingService.send('bgUpdateContextMenu');
|
||||
}
|
||||
async updateDisableContextMenuItem() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.disableContextMenuItemKey,
|
||||
this.disableContextMenuItem
|
||||
);
|
||||
this.messagingService.send("bgUpdateContextMenu");
|
||||
}
|
||||
|
||||
async updateAutoTotpCopy() {
|
||||
await this.storageService.save(ConstantsService.disableAutoTotpCopyKey, this.disableAutoTotpCopy);
|
||||
}
|
||||
async updateAutoTotpCopy() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.disableAutoTotpCopyKey,
|
||||
this.disableAutoTotpCopy
|
||||
);
|
||||
}
|
||||
|
||||
async updateAutoFillOnPageLoad() {
|
||||
await this.storageService.save(ConstantsService.enableAutoFillOnPageLoadKey, this.enableAutoFillOnPageLoad);
|
||||
}
|
||||
async updateAutoFillOnPageLoad() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.enableAutoFillOnPageLoadKey,
|
||||
this.enableAutoFillOnPageLoad
|
||||
);
|
||||
}
|
||||
|
||||
async updateAutoFillOnPageLoadDefault() {
|
||||
await this.storageService.save(ConstantsService.autoFillOnPageLoadDefaultKey, this.autoFillOnPageLoadDefault);
|
||||
}
|
||||
async updateAutoFillOnPageLoadDefault() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.autoFillOnPageLoadDefaultKey,
|
||||
this.autoFillOnPageLoadDefault
|
||||
);
|
||||
}
|
||||
|
||||
async updateDisableFavicon() {
|
||||
await this.storageService.save(ConstantsService.disableFaviconKey, this.disableFavicon);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, this.disableFavicon);
|
||||
}
|
||||
async updateDisableFavicon() {
|
||||
await this.storageService.save(ConstantsService.disableFaviconKey, this.disableFavicon);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, this.disableFavicon);
|
||||
}
|
||||
|
||||
async updateDisableBadgeCounter() {
|
||||
await this.storageService.save(ConstantsService.disableBadgeCounterKey, this.disableBadgeCounter);
|
||||
await this.stateService.save(ConstantsService.disableBadgeCounterKey, this.disableBadgeCounter);
|
||||
this.messagingService.send('bgUpdateContextMenu');
|
||||
}
|
||||
async updateDisableBadgeCounter() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.disableBadgeCounterKey,
|
||||
this.disableBadgeCounter
|
||||
);
|
||||
await this.stateService.save(ConstantsService.disableBadgeCounterKey, this.disableBadgeCounter);
|
||||
this.messagingService.send("bgUpdateContextMenu");
|
||||
}
|
||||
|
||||
async updateShowCards() {
|
||||
await this.storageService.save(ConstantsService.dontShowCardsCurrentTab, this.dontShowCards);
|
||||
await this.stateService.save(ConstantsService.dontShowCardsCurrentTab, this.dontShowCards);
|
||||
}
|
||||
async updateShowCards() {
|
||||
await this.storageService.save(ConstantsService.dontShowCardsCurrentTab, this.dontShowCards);
|
||||
await this.stateService.save(ConstantsService.dontShowCardsCurrentTab, this.dontShowCards);
|
||||
}
|
||||
|
||||
async updateShowIdentities() {
|
||||
await this.storageService.save(ConstantsService.dontShowIdentitiesCurrentTab, this.dontShowIdentities);
|
||||
await this.stateService.save(ConstantsService.dontShowIdentitiesCurrentTab, this.dontShowIdentities);
|
||||
}
|
||||
async updateShowIdentities() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.dontShowIdentitiesCurrentTab,
|
||||
this.dontShowIdentities
|
||||
);
|
||||
await this.stateService.save(
|
||||
ConstantsService.dontShowIdentitiesCurrentTab,
|
||||
this.dontShowIdentities
|
||||
);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
await this.storageService.save(ConstantsService.themeKey, this.theme);
|
||||
window.setTimeout(() => window.location.reload(), 200);
|
||||
}
|
||||
async saveTheme() {
|
||||
await this.storageService.save(ConstantsService.themeKey, this.theme);
|
||||
window.setTimeout(() => window.location.reload(), 200);
|
||||
}
|
||||
|
||||
async saveDefaultUriMatch() {
|
||||
await this.storageService.save(ConstantsService.defaultUriMatch, this.defaultUriMatch);
|
||||
}
|
||||
async saveDefaultUriMatch() {
|
||||
await this.storageService.save(ConstantsService.defaultUriMatch, this.defaultUriMatch);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
await this.storageService.save(ConstantsService.clearClipboardKey, this.clearClipboard);
|
||||
}
|
||||
async saveClearClipboard() {
|
||||
await this.storageService.save(ConstantsService.clearClipboardKey, this.clearClipboard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,73 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'premiumMembership' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "premiumMembership" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="content">
|
||||
<ng-container *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{'premiumNotCurrentMember' | i18n}}</p>
|
||||
<p>{{'premiumSignUpAndGet' | i18n}}</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'ppremiumSignUpStorage' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'ppremiumSignUpTwoStep' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'ppremiumSignUpReports' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'ppremiumSignUpTotp' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'ppremiumSignUpSupport' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'ppremiumSignUpFuture' | i18n}}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead">{{priceString}}</p>
|
||||
<button type="button" class="btn primary block" appBlurClick (click)="purchase()">
|
||||
<b>{{'premiumPurchase' | i18n}}</b>
|
||||
</button>
|
||||
<button #refreshBtn type="button" appBlurClick (click)="refresh()" [disabled]="refreshBtn.loading"
|
||||
[appApiAction]="refreshPromise" class="btn link block">
|
||||
<span [hidden]="refreshBtn.loading">{{'premiumRefresh' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!refreshBtn.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isPremium">
|
||||
<p class="text-center lead">{{'premiumCurrentMember' | i18n}}</p>
|
||||
<p class="text-center">{{'premiumCurrentMemberThanks' | i18n}}</p>
|
||||
<button type="button" class="btn block primary" appBlurClick (click)="manage()">
|
||||
<b>{{'premiumManage' | i18n}}</b>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="content">
|
||||
<ng-container *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
|
||||
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "ppremiumSignUpStorage" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "ppremiumSignUpTwoStep" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "ppremiumSignUpReports" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "ppremiumSignUpTotp" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "ppremiumSignUpSupport" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "ppremiumSignUpFuture" | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead">{{ priceString }}</p>
|
||||
<button type="button" class="btn primary block" appBlurClick (click)="purchase()">
|
||||
<b>{{ "premiumPurchase" | i18n }}</b>
|
||||
</button>
|
||||
<button
|
||||
#refreshBtn
|
||||
type="button"
|
||||
appBlurClick
|
||||
(click)="refresh()"
|
||||
[disabled]="refreshBtn.loading"
|
||||
[appApiAction]="refreshPromise"
|
||||
class="btn link block"
|
||||
>
|
||||
<span [hidden]="refreshBtn.loading">{{ "premiumRefresh" | i18n }}</span>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!refreshBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isPremium">
|
||||
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
|
||||
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
|
||||
<button type="button" class="btn block primary" appBlurClick (click)="manage()">
|
||||
<b>{{ "premiumManage" | i18n }}</b>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
import { CurrencyPipe } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { CurrencyPipe } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import { PremiumComponent as BasePremiumComponent } from 'jslib-angular/components/premium.component';
|
||||
import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-premium',
|
||||
templateUrl: 'premium.component.html',
|
||||
selector: "app-premium",
|
||||
templateUrl: "premium.component.html",
|
||||
})
|
||||
export class PremiumComponent extends BasePremiumComponent {
|
||||
priceString: string;
|
||||
priceString: string;
|
||||
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService, userService: UserService,
|
||||
private currencyPipe: CurrencyPipe, logService: LogService) {
|
||||
super(i18nService, platformUtilsService, apiService, userService, logService);
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
userService: UserService,
|
||||
private currencyPipe: CurrencyPipe,
|
||||
logService: LogService
|
||||
) {
|
||||
super(i18nService, platformUtilsService, apiService, userService, logService);
|
||||
|
||||
// Support old price string. Can be removed in future once all translations are properly updated.
|
||||
const thePrice = this.currencyPipe.transform(this.price, '$');
|
||||
this.priceString = i18nService.t('premiumPrice', thePrice);
|
||||
if (this.priceString.indexOf('%price%') > -1) {
|
||||
this.priceString = this.priceString.replace('%price%', thePrice);
|
||||
}
|
||||
// Support old price string. Can be removed in future once all translations are properly updated.
|
||||
const thePrice = this.currencyPipe.transform(this.price, "$");
|
||||
this.priceString = i18nService.t("premiumPrice", thePrice);
|
||||
if (this.priceString.indexOf("%price%") > -1) {
|
||||
this.priceString = this.priceString.replace("%price%", thePrice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +1,225 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'settings' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
<div class="left">
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "settings" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{'manage' | i18n}}</h2>
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/folders">
|
||||
<div class="row-main">{{'folders' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/sync">
|
||||
<div class="row-main">{{'sync' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/excluded-domains">
|
||||
<div class="row-main">{{'excludedDomains' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "manage" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/folders">
|
||||
<div class="row-main">{{ "folders" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/sync">
|
||||
<div class="row-main">{{ "sync" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/excluded-domains">
|
||||
<div class="row-main">{{ "excludedDomains" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{'security' | i18n}}</h2>
|
||||
<div class="box-content single-line">
|
||||
<app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl></app-vault-timeout-input>
|
||||
<div class="box-content-row display-block" appBoxRow>
|
||||
<label for="vaultTimeoutAction">{{'vaultTimeoutAction' | i18n}}</label>
|
||||
<select #vaultTimeoutActionSelect id="vaultTimeoutAction" name="VaultTimeoutActions"
|
||||
[ngModel]="vaultTimeoutAction" (ngModelChange)="saveVaultTimeoutAction($event)">
|
||||
<option *ngFor="let o of vaultTimeoutActions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="pin">{{'unlockWithPin' | i18n}}</label>
|
||||
<input id="pin" type="checkbox" (change)="updatePin()" [(ngModel)]="pin">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="supportsBiometric">
|
||||
<label for="biometric">{{'unlockWithBiometrics' | i18n}}</label>
|
||||
<input id="biometric" type="checkbox" (change)="updateBiometric()" [(ngModel)]="biometric">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="supportsBiometric">
|
||||
<label for="autoBiometricsPrompt">{{'disableAutoBiometricsPrompt' | i18n}}</label>
|
||||
<input id="autoBiometricsPrompt" type="checkbox" (change)="updateAutoBiometricsPrompt()" [disabled]="!biometric" [(ngModel)]="disableAutoBiometricsPrompt">
|
||||
</div>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="lock()">
|
||||
<div class="row-main">{{'lockNow' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="twoStep()">
|
||||
<div class="row-main">{{'twoStepLogin' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "security" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<app-vault-timeout-input
|
||||
[vaultTimeouts]="vaultTimeouts"
|
||||
[formControl]="vaultTimeout"
|
||||
ngDefaultControl
|
||||
></app-vault-timeout-input>
|
||||
<div class="box-content-row display-block" appBoxRow>
|
||||
<label for="vaultTimeoutAction">{{ "vaultTimeoutAction" | i18n }}</label>
|
||||
<select
|
||||
#vaultTimeoutActionSelect
|
||||
id="vaultTimeoutAction"
|
||||
name="VaultTimeoutActions"
|
||||
[ngModel]="vaultTimeoutAction"
|
||||
(ngModelChange)="saveVaultTimeoutAction($event)"
|
||||
>
|
||||
<option *ngFor="let o of vaultTimeoutActions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="pin">{{ "unlockWithPin" | i18n }}</label>
|
||||
<input id="pin" type="checkbox" (change)="updatePin()" [(ngModel)]="pin" />
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="supportsBiometric">
|
||||
<label for="biometric">{{ "unlockWithBiometrics" | i18n }}</label>
|
||||
<input
|
||||
id="biometric"
|
||||
type="checkbox"
|
||||
(change)="updateBiometric()"
|
||||
[(ngModel)]="biometric"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="supportsBiometric">
|
||||
<label for="autoBiometricsPrompt">{{ "disableAutoBiometricsPrompt" | i18n }}</label>
|
||||
<input
|
||||
id="autoBiometricsPrompt"
|
||||
type="checkbox"
|
||||
(change)="updateAutoBiometricsPrompt()"
|
||||
[disabled]="!biometric"
|
||||
[(ngModel)]="disableAutoBiometricsPrompt"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="lock()"
|
||||
>
|
||||
<div class="row-main">{{ "lockNow" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="twoStep()"
|
||||
>
|
||||
<div class="row-main">{{ "twoStepLogin" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{'account' | i18n}}</h2>
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row" routerLink="/premium">
|
||||
<div class="row-main">
|
||||
<div class="icon text-primary"><i class="fa fa-fw fa-lg fa-star" aria-hidden="true"></i></div>
|
||||
<span class="text text-primary"><b>{{'premiumMembership' | i18n}}</b></span>
|
||||
</div>
|
||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i></span>
|
||||
</a>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="changePassword()" *ngIf="showChangeMasterPass">
|
||||
<div class="row-main">{{'changeMasterPassword' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="fingerprint()">
|
||||
<div class="row-main">{{'fingerprintPhrase' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="logOut()">
|
||||
<div class="row-main">{{'logOut' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "account" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row" routerLink="/premium">
|
||||
<div class="row-main">
|
||||
<div class="icon text-primary">
|
||||
<i class="fa fa-fw fa-lg fa-star" aria-hidden="true"></i>
|
||||
</div>
|
||||
<span class="text text-primary"
|
||||
><b>{{ "premiumMembership" | i18n }}</b></span
|
||||
>
|
||||
</div>
|
||||
<span><i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i></span>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="changePassword()"
|
||||
*ngIf="showChangeMasterPass"
|
||||
>
|
||||
<div class="row-main">{{ "changeMasterPassword" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="fingerprint()"
|
||||
>
|
||||
<div class="row-main">{{ "fingerprintPhrase" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="logOut()"
|
||||
>
|
||||
<div class="row-main">{{ "logOut" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{'tools' | i18n}}</h2>
|
||||
<div class="box-content single-line">
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="import()">
|
||||
<div class="row-main">{{'importItems' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="export()">
|
||||
<div class="row-main">{{'exportVault' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="webVault()">
|
||||
<div class="row-main">{{'bitWebVault' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "tools" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="import()"
|
||||
>
|
||||
<div class="row-main">{{ "importItems" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="export()"
|
||||
>
|
||||
<div class="row-main">{{ "exportVault" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="webVault()"
|
||||
>
|
||||
<div class="row-main">{{ "bitWebVault" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{'other' | i18n}}</h2>
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/options">
|
||||
<div class="row-main">{{'options' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="about()">
|
||||
<div class="row-main">{{'about' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="share()">
|
||||
<div class="row-main">{{'learnOrg' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="help()">
|
||||
<div class="row-main">{{'helpFeedback' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="rate()">
|
||||
<div class="row-main">{{'rateExtension' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-footer">{{'rateExtensionDesc' | i18n}}</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "other" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row box-content-row-flex text-default" routerLink="/options">
|
||||
<div class="row-main">{{ "options" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="about()"
|
||||
>
|
||||
<div class="row-main">{{ "about" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="share()"
|
||||
>
|
||||
<div class="row-main">{{ "learnOrg" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="help()"
|
||||
>
|
||||
<div class="row-main">{{ "helpFeedback" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="rate()"
|
||||
>
|
||||
<div class="row-main">{{ "rateExtension" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right fa-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-footer">{{ "rateExtensionDesc" | i18n }}</div>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,368 +1,416 @@
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import Swal from 'sweetalert2/src/sweetalert2.js';
|
||||
import { Component, ElementRef, OnInit, ViewChild } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import Swal from "sweetalert2/src/sweetalert2.js";
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
import { DeviceType } from 'jslib-common/enums/deviceType';
|
||||
import { DeviceType } from "jslib-common/enums/deviceType";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { SetPinComponent } from '../components/set-pin.component';
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
|
||||
const RateUrls = {
|
||||
[DeviceType.ChromeExtension]:
|
||||
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews',
|
||||
[DeviceType.FirefoxExtension]:
|
||||
'https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/#reviews',
|
||||
[DeviceType.OperaExtension]:
|
||||
'https://addons.opera.com/en/extensions/details/bitwarden-free-password-manager/#feedback-container',
|
||||
[DeviceType.EdgeExtension]:
|
||||
'https://microsoftedge.microsoft.com/addons/detail/jbkfoedolllekgbhcbcoahefnbanhhlh',
|
||||
[DeviceType.VivaldiExtension]:
|
||||
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews',
|
||||
[DeviceType.SafariExtension]:
|
||||
'https://apps.apple.com/app/bitwarden/id1352778147',
|
||||
[DeviceType.ChromeExtension]:
|
||||
"https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews",
|
||||
[DeviceType.FirefoxExtension]:
|
||||
"https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/#reviews",
|
||||
[DeviceType.OperaExtension]:
|
||||
"https://addons.opera.com/en/extensions/details/bitwarden-free-password-manager/#feedback-container",
|
||||
[DeviceType.EdgeExtension]:
|
||||
"https://microsoftedge.microsoft.com/addons/detail/jbkfoedolllekgbhcbcoahefnbanhhlh",
|
||||
[DeviceType.VivaldiExtension]:
|
||||
"https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews",
|
||||
[DeviceType.SafariExtension]: "https://apps.apple.com/app/bitwarden/id1352778147",
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: 'settings.component.html',
|
||||
selector: "app-settings",
|
||||
templateUrl: "settings.component.html",
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
@ViewChild('vaultTimeoutActionSelect', { read: ElementRef, static: true }) vaultTimeoutActionSelectRef: ElementRef;
|
||||
vaultTimeouts: any[];
|
||||
vaultTimeoutActions: any[];
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean = false;
|
||||
disableAutoBiometricsPrompt = true;
|
||||
previousVaultTimeout: number = null;
|
||||
showChangeMasterPass = true;
|
||||
@ViewChild("vaultTimeoutActionSelect", { read: ElementRef, static: true })
|
||||
vaultTimeoutActionSelectRef: ElementRef;
|
||||
vaultTimeouts: any[];
|
||||
vaultTimeoutActions: any[];
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean = false;
|
||||
disableAutoBiometricsPrompt = true;
|
||||
previousVaultTimeout: number = null;
|
||||
showChangeMasterPass = true;
|
||||
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||
private vaultTimeoutService: VaultTimeoutService, private storageService: StorageService,
|
||||
public messagingService: MessagingService, private router: Router,
|
||||
private environmentService: EnvironmentService, private cryptoService: CryptoService,
|
||||
private userService: UserService, private popupUtilsService: PopupUtilsService,
|
||||
private modalService: ModalService,
|
||||
private keyConnectorService: KeyConnectorService) {
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private storageService: StorageService,
|
||||
public messagingService: MessagingService,
|
||||
private router: Router,
|
||||
private environmentService: EnvironmentService,
|
||||
private cryptoService: CryptoService,
|
||||
private userService: UserService,
|
||||
private popupUtilsService: PopupUtilsService,
|
||||
private modalService: ModalService,
|
||||
private keyConnectorService: KeyConnectorService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
const showOnLocked =
|
||||
!this.platformUtilsService.isFirefox() && !this.platformUtilsService.isSafari();
|
||||
|
||||
this.vaultTimeouts = [
|
||||
{ name: this.i18nService.t("immediately"), value: 0 },
|
||||
{ name: this.i18nService.t("oneMinute"), value: 1 },
|
||||
{ name: this.i18nService.t("fiveMinutes"), value: 5 },
|
||||
{ name: this.i18nService.t("fifteenMinutes"), value: 15 },
|
||||
{ name: this.i18nService.t("thirtyMinutes"), value: 30 },
|
||||
{ name: this.i18nService.t("oneHour"), value: 60 },
|
||||
{ name: this.i18nService.t("fourHours"), value: 240 },
|
||||
// { name: i18nService.t('onIdle'), value: -4 },
|
||||
// { name: i18nService.t('onSleep'), value: -3 },
|
||||
];
|
||||
|
||||
if (showOnLocked) {
|
||||
this.vaultTimeouts.push({ name: this.i18nService.t("onLocked"), value: -2 });
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
const showOnLocked = !this.platformUtilsService.isFirefox() && !this.platformUtilsService.isSafari();
|
||||
this.vaultTimeouts.push({ name: this.i18nService.t("onRestart"), value: -1 });
|
||||
this.vaultTimeouts.push({ name: this.i18nService.t("never"), value: null });
|
||||
|
||||
this.vaultTimeouts = [
|
||||
{ name: this.i18nService.t('immediately'), value: 0 },
|
||||
{ name: this.i18nService.t('oneMinute'), value: 1 },
|
||||
{ name: this.i18nService.t('fiveMinutes'), value: 5 },
|
||||
{ name: this.i18nService.t('fifteenMinutes'), value: 15 },
|
||||
{ name: this.i18nService.t('thirtyMinutes'), value: 30 },
|
||||
{ name: this.i18nService.t('oneHour'), value: 60 },
|
||||
{ name: this.i18nService.t('fourHours'), value: 240 },
|
||||
// { name: i18nService.t('onIdle'), value: -4 },
|
||||
// { name: i18nService.t('onSleep'), value: -3 },
|
||||
];
|
||||
this.vaultTimeoutActions = [
|
||||
{ name: this.i18nService.t("lock"), value: "lock" },
|
||||
{ name: this.i18nService.t("logOut"), value: "logOut" },
|
||||
];
|
||||
|
||||
if (showOnLocked) {
|
||||
this.vaultTimeouts.push({ name: this.i18nService.t('onLocked'), value: -2 });
|
||||
}
|
||||
let timeout = await this.vaultTimeoutService.getVaultTimeout();
|
||||
if (timeout != null) {
|
||||
if (timeout === -2 && !showOnLocked) {
|
||||
timeout = -1;
|
||||
}
|
||||
this.vaultTimeout.setValue(timeout);
|
||||
}
|
||||
this.previousVaultTimeout = this.vaultTimeout.value;
|
||||
this.vaultTimeout.valueChanges.subscribe((value) => {
|
||||
this.saveVaultTimeout(value);
|
||||
});
|
||||
|
||||
this.vaultTimeouts.push({ name: this.i18nService.t('onRestart'), value: -1 });
|
||||
this.vaultTimeouts.push({ name: this.i18nService.t('never'), value: null });
|
||||
const action = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
|
||||
this.vaultTimeoutAction = action == null ? "lock" : action;
|
||||
|
||||
this.vaultTimeoutActions = [
|
||||
{ name: this.i18nService.t('lock'), value: 'lock' },
|
||||
{ name: this.i18nService.t('logOut'), value: 'logOut' },
|
||||
];
|
||||
const pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pin = pinSet[0] || pinSet[1];
|
||||
|
||||
let timeout = await this.vaultTimeoutService.getVaultTimeout();
|
||||
if (timeout != null) {
|
||||
if (timeout === -2 && !showOnLocked) {
|
||||
timeout = -1;
|
||||
}
|
||||
this.vaultTimeout.setValue(timeout);
|
||||
}
|
||||
this.previousVaultTimeout = this.vaultTimeout.value;
|
||||
this.vaultTimeout.valueChanges.subscribe(value => {
|
||||
this.saveVaultTimeout(value);
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.disableAutoBiometricsPrompt =
|
||||
(await this.storageService.get<boolean>(ConstantsService.disableAutoBiometricsPromptKey)) ??
|
||||
true;
|
||||
this.showChangeMasterPass = !(await this.keyConnectorService.getUsesKeyConnector());
|
||||
}
|
||||
|
||||
async saveVaultTimeout(newValue: number) {
|
||||
if (newValue == null) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("neverLockWarning"),
|
||||
null,
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel"),
|
||||
"warning"
|
||||
);
|
||||
if (!confirmed) {
|
||||
this.vaultTimeout.setValue(this.previousVaultTimeout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("vaultTimeoutToLarge"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousVaultTimeout = this.vaultTimeout.value;
|
||||
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(
|
||||
this.vaultTimeout.value,
|
||||
this.vaultTimeoutAction
|
||||
);
|
||||
if (this.previousVaultTimeout == null) {
|
||||
this.messagingService.send("bgReseedStorage");
|
||||
}
|
||||
}
|
||||
|
||||
async saveVaultTimeoutAction(newValue: string) {
|
||||
if (newValue === "logOut") {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("vaultTimeoutLogOutConfirmation"),
|
||||
this.i18nService.t("vaultTimeoutLogOutConfirmationTitle"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel"),
|
||||
"warning"
|
||||
);
|
||||
if (!confirmed) {
|
||||
this.vaultTimeoutActions.forEach((option: any, i) => {
|
||||
if (option.value === this.vaultTimeoutAction) {
|
||||
this.vaultTimeoutActionSelectRef.nativeElement.value =
|
||||
i + ": " + this.vaultTimeoutAction;
|
||||
}
|
||||
});
|
||||
|
||||
const action = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
|
||||
this.vaultTimeoutAction = action == null ? 'lock' : action;
|
||||
|
||||
const pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pin = pinSet[0] || pinSet[1];
|
||||
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.disableAutoBiometricsPrompt = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableAutoBiometricsPromptKey) ?? true;
|
||||
this.showChangeMasterPass = !await this.keyConnectorService.getUsesKeyConnector();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async saveVaultTimeout(newValue: number) {
|
||||
if (newValue == null) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('neverLockWarning'), null,
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'), 'warning');
|
||||
if (!confirmed) {
|
||||
this.vaultTimeout.setValue(this.previousVaultTimeout);
|
||||
return;
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("vaultTimeoutToLarge"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.vaultTimeoutAction = newValue;
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(
|
||||
this.vaultTimeout.value,
|
||||
this.vaultTimeoutAction
|
||||
);
|
||||
}
|
||||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
} else {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async updateBiometric() {
|
||||
if (this.biometric && this.supportsBiometric) {
|
||||
let granted;
|
||||
try {
|
||||
granted = await BrowserApi.requestPermission({ permissions: ["nativeMessaging"] });
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line
|
||||
console.error(e);
|
||||
|
||||
if (this.platformUtilsService.isFirefox() && this.popupUtilsService.inSidebar(window)) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("nativeMessaginPermissionSidebarDesc"),
|
||||
this.i18nService.t("nativeMessaginPermissionSidebarTitle"),
|
||||
this.i18nService.t("ok"),
|
||||
null
|
||||
);
|
||||
this.biometric = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!granted) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("nativeMessaginPermissionErrorDesc"),
|
||||
this.i18nService.t("nativeMessaginPermissionErrorTitle"),
|
||||
this.i18nService.t("ok"),
|
||||
null
|
||||
);
|
||||
this.biometric = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const submitted = Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
titleText: this.i18nService.t("awaitDesktop"),
|
||||
text: this.i18nService.t("awaitDesktopDesc"),
|
||||
icon: "info",
|
||||
iconHtml: '<i class="swal-custom-icon fa fa-info-circle text-info"></i>',
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t("cancel"),
|
||||
showConfirmButton: false,
|
||||
allowOutsideClick: false,
|
||||
});
|
||||
|
||||
await this.storageService.save(ConstantsService.biometricAwaitingAcceptance, true);
|
||||
await this.cryptoService.toggleKey();
|
||||
|
||||
await Promise.race([
|
||||
submitted.then((result) => {
|
||||
if (result.dismiss === Swal.DismissReason.cancel) {
|
||||
this.biometric = false;
|
||||
this.storageService.remove(ConstantsService.biometricAwaitingAcceptance);
|
||||
}
|
||||
}),
|
||||
this.platformUtilsService
|
||||
.authenticateBiometric()
|
||||
.then((result) => {
|
||||
this.biometric = result;
|
||||
|
||||
Swal.close();
|
||||
if (this.biometric === false) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorEnableBiometricTitle"),
|
||||
this.i18nService.t("errorEnableBiometricDesc")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutToLarge'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousVaultTimeout = this.vaultTimeout.value;
|
||||
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
if (this.previousVaultTimeout == null) {
|
||||
this.messagingService.send('bgReseedStorage');
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
// Handle connection errors
|
||||
this.biometric = false;
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
await this.storageService.remove(ConstantsService.biometricUnlockKey);
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
async saveVaultTimeoutAction(newValue: string) {
|
||||
if (newValue === 'logOut') {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('vaultTimeoutLogOutConfirmation'),
|
||||
this.i18nService.t('vaultTimeoutLogOutConfirmationTitle'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'), 'warning');
|
||||
if (!confirmed) {
|
||||
this.vaultTimeoutActions.forEach((option: any, i) => {
|
||||
if (option.value === this.vaultTimeoutAction) {
|
||||
this.vaultTimeoutActionSelectRef.nativeElement.value = i + ': ' + this.vaultTimeoutAction;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
async updateAutoBiometricsPrompt() {
|
||||
await this.storageService.save(
|
||||
ConstantsService.disableAutoBiometricsPromptKey,
|
||||
this.disableAutoBiometricsPrompt
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutToLarge'));
|
||||
return;
|
||||
}
|
||||
async lock() {
|
||||
await this.vaultTimeoutService.lock(true);
|
||||
}
|
||||
|
||||
this.vaultTimeoutAction = newValue;
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
async logOut() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("logOutConfirmation"),
|
||||
this.i18nService.t("logOut"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel")
|
||||
);
|
||||
if (confirmed) {
|
||||
this.messagingService.send("logout");
|
||||
}
|
||||
}
|
||||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
} else {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
async changePassword() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("changeMasterPasswordConfirmation"),
|
||||
this.i18nService.t("changeMasterPassword"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel")
|
||||
);
|
||||
if (confirmed) {
|
||||
BrowserApi.createNewTab("https://help.bitwarden.com/article/change-your-master-password/");
|
||||
}
|
||||
}
|
||||
|
||||
async updateBiometric() {
|
||||
if (this.biometric && this.supportsBiometric) {
|
||||
|
||||
let granted;
|
||||
try {
|
||||
granted = await BrowserApi.requestPermission({ permissions: ['nativeMessaging'] });
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line
|
||||
console.error(e);
|
||||
|
||||
if (this.platformUtilsService.isFirefox() && this.popupUtilsService.inSidebar(window)) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('nativeMessaginPermissionSidebarDesc'), this.i18nService.t('nativeMessaginPermissionSidebarTitle'),
|
||||
this.i18nService.t('ok'), null);
|
||||
this.biometric = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!granted) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('nativeMessaginPermissionErrorDesc'), this.i18nService.t('nativeMessaginPermissionErrorTitle'),
|
||||
this.i18nService.t('ok'), null);
|
||||
this.biometric = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const submitted = Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
titleText: this.i18nService.t('awaitDesktop'),
|
||||
text: this.i18nService.t('awaitDesktopDesc'),
|
||||
icon: 'info',
|
||||
iconHtml: '<i class="swal-custom-icon fa fa-info-circle text-info"></i>',
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t('cancel'),
|
||||
showConfirmButton: false,
|
||||
allowOutsideClick: false,
|
||||
});
|
||||
|
||||
await this.storageService.save(ConstantsService.biometricAwaitingAcceptance, true);
|
||||
await this.cryptoService.toggleKey();
|
||||
|
||||
await Promise.race([
|
||||
submitted.then(result => {
|
||||
if (result.dismiss === Swal.DismissReason.cancel) {
|
||||
this.biometric = false;
|
||||
this.storageService.remove(ConstantsService.biometricAwaitingAcceptance);
|
||||
}
|
||||
}),
|
||||
this.platformUtilsService.authenticateBiometric().then(result => {
|
||||
this.biometric = result;
|
||||
|
||||
Swal.close();
|
||||
if (this.biometric === false) {
|
||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorEnableBiometricTitle'), this.i18nService.t('errorEnableBiometricDesc'));
|
||||
}
|
||||
}).catch(e => {
|
||||
// Handle connection errors
|
||||
this.biometric = false;
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
await this.storageService.remove(ConstantsService.biometricUnlockKey);
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
}
|
||||
async twoStep() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("twoStepLoginConfirmation"),
|
||||
this.i18nService.t("twoStepLogin"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel")
|
||||
);
|
||||
if (confirmed) {
|
||||
BrowserApi.createNewTab("https://help.bitwarden.com/article/setup-two-step-login/");
|
||||
}
|
||||
}
|
||||
|
||||
async updateAutoBiometricsPrompt() {
|
||||
await this.storageService.save(ConstantsService.disableAutoBiometricsPromptKey, this.disableAutoBiometricsPrompt);
|
||||
async share() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("learnOrgConfirmation"),
|
||||
this.i18nService.t("learnOrg"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel")
|
||||
);
|
||||
if (confirmed) {
|
||||
BrowserApi.createNewTab("https://help.bitwarden.com/article/what-is-an-organization/");
|
||||
}
|
||||
}
|
||||
|
||||
async lock() {
|
||||
await this.vaultTimeoutService.lock(true);
|
||||
async webVault() {
|
||||
const url = this.environmentService.getWebVaultUrl();
|
||||
BrowserApi.createNewTab(url);
|
||||
}
|
||||
|
||||
import() {
|
||||
BrowserApi.createNewTab("https://help.bitwarden.com/article/import-data/");
|
||||
}
|
||||
|
||||
export() {
|
||||
this.router.navigate(["/export"]);
|
||||
}
|
||||
|
||||
help() {
|
||||
BrowserApi.createNewTab("https://help.bitwarden.com/");
|
||||
}
|
||||
|
||||
about() {
|
||||
const year = new Date().getFullYear();
|
||||
const versionText = document.createTextNode(
|
||||
this.i18nService.t("version") + ": " + BrowserApi.getApplicationVersion()
|
||||
);
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML =
|
||||
`<p class="text-center"><i class="fa fa-shield fa-3x" aria-hidden="true"></i></p>
|
||||
<p class="text-center"><b>Bitwarden</b><br>© Bitwarden Inc. 2015-` +
|
||||
year +
|
||||
`</p>`;
|
||||
div.appendChild(versionText);
|
||||
|
||||
Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showConfirmButton: false,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t("close"),
|
||||
});
|
||||
}
|
||||
|
||||
async fingerprint() {
|
||||
const fingerprint = await this.cryptoService.getFingerprint(await this.userService.getUserId());
|
||||
const p = document.createElement("p");
|
||||
p.innerText = this.i18nService.t("yourAccountsFingerprint") + ":";
|
||||
const p2 = document.createElement("p");
|
||||
p2.innerText = fingerprint.join("-");
|
||||
const div = document.createElement("div");
|
||||
div.appendChild(p);
|
||||
div.appendChild(p2);
|
||||
|
||||
const result = await Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t("close"),
|
||||
showConfirmButton: true,
|
||||
confirmButtonText: this.i18nService.t("learnMore"),
|
||||
});
|
||||
|
||||
if (result.value) {
|
||||
this.platformUtilsService.launchUri("https://help.bitwarden.com/article/fingerprint-phrase/");
|
||||
}
|
||||
}
|
||||
|
||||
async logOut() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('logOutConfirmation'), this.i18nService.t('logOut'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'));
|
||||
if (confirmed) {
|
||||
this.messagingService.send('logout');
|
||||
}
|
||||
}
|
||||
|
||||
async changePassword() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('changeMasterPasswordConfirmation'), this.i18nService.t('changeMasterPassword'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'));
|
||||
if (confirmed) {
|
||||
BrowserApi.createNewTab('https://help.bitwarden.com/article/change-your-master-password/');
|
||||
}
|
||||
}
|
||||
|
||||
async twoStep() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('twoStepLoginConfirmation'), this.i18nService.t('twoStepLogin'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'));
|
||||
if (confirmed) {
|
||||
BrowserApi.createNewTab('https://help.bitwarden.com/article/setup-two-step-login/');
|
||||
}
|
||||
}
|
||||
|
||||
async share() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('learnOrgConfirmation'), this.i18nService.t('learnOrg'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'));
|
||||
if (confirmed) {
|
||||
BrowserApi.createNewTab('https://help.bitwarden.com/article/what-is-an-organization/');
|
||||
}
|
||||
}
|
||||
|
||||
async webVault() {
|
||||
const url = this.environmentService.getWebVaultUrl();
|
||||
BrowserApi.createNewTab(url);
|
||||
}
|
||||
|
||||
import() {
|
||||
BrowserApi.createNewTab('https://help.bitwarden.com/article/import-data/');
|
||||
}
|
||||
|
||||
export() {
|
||||
this.router.navigate(['/export']);
|
||||
}
|
||||
|
||||
help() {
|
||||
BrowserApi.createNewTab('https://help.bitwarden.com/');
|
||||
}
|
||||
|
||||
about() {
|
||||
const year = (new Date()).getFullYear();
|
||||
const versionText = document.createTextNode(
|
||||
this.i18nService.t('version') + ': ' + BrowserApi.getApplicationVersion());
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `<p class="text-center"><i class="fa fa-shield fa-3x" aria-hidden="true"></i></p>
|
||||
<p class="text-center"><b>Bitwarden</b><br>© Bitwarden Inc. 2015-` + year + `</p>`;
|
||||
div.appendChild(versionText);
|
||||
|
||||
Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showConfirmButton: false,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t('close'),
|
||||
});
|
||||
}
|
||||
|
||||
async fingerprint() {
|
||||
const fingerprint = await this.cryptoService.getFingerprint(await this.userService.getUserId());
|
||||
const p = document.createElement('p');
|
||||
p.innerText = this.i18nService.t('yourAccountsFingerprint') + ':';
|
||||
const p2 = document.createElement('p');
|
||||
p2.innerText = fingerprint.join('-');
|
||||
const div = document.createElement('div');
|
||||
div.appendChild(p);
|
||||
div.appendChild(p2);
|
||||
|
||||
const result = await Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t('close'),
|
||||
showConfirmButton: true,
|
||||
confirmButtonText: this.i18nService.t('learnMore'),
|
||||
});
|
||||
|
||||
if (result.value) {
|
||||
this.platformUtilsService.launchUri('https://help.bitwarden.com/article/fingerprint-phrase/');
|
||||
}
|
||||
}
|
||||
|
||||
rate() {
|
||||
const deviceType = this.platformUtilsService.getDevice();
|
||||
BrowserApi.createNewTab((RateUrls as any)[deviceType]);
|
||||
}
|
||||
rate() {
|
||||
const deviceType = this.platformUtilsService.getDevice();
|
||||
BrowserApi.createNewTab((RateUrls as any)[deviceType]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{'sync' | i18n}}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="fa fa-chevron-left" aria-hidden="true"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "sync" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="content center-content">
|
||||
<button type="button" class="btn block primary" (click)="sync()" #syncBtn [disabled]="syncBtn.loading"
|
||||
[appApiAction]="syncPromise">
|
||||
<span [hidden]="syncBtn.loading">{{'syncVaultNow' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!syncBtn.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<p class="text-center text-muted small">{{'lastSync' | i18n}} {{lastSync}}</p>
|
||||
</div>
|
||||
<div class="content center-content">
|
||||
<button
|
||||
type="button"
|
||||
class="btn block primary"
|
||||
(click)="sync()"
|
||||
#syncBtn
|
||||
[disabled]="syncBtn.loading"
|
||||
[appApiAction]="syncPromise"
|
||||
>
|
||||
<span [hidden]="syncBtn.loading">{{ "syncVaultNow" | i18n }}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!syncBtn.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<p class="text-center text-muted small">{{ "lastSync" | i18n }} {{ lastSync }}</p>
|
||||
</div>
|
||||
</content>
|
||||
|
||||
@@ -1,45 +1,44 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-sync',
|
||||
templateUrl: 'sync.component.html',
|
||||
selector: "app-sync",
|
||||
templateUrl: "sync.component.html",
|
||||
})
|
||||
export class SyncComponent implements OnInit {
|
||||
lastSync = '--';
|
||||
syncPromise: Promise<any>;
|
||||
lastSync = "--";
|
||||
syncPromise: Promise<any>;
|
||||
|
||||
constructor(private syncService: SyncService, private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService) {
|
||||
}
|
||||
constructor(
|
||||
private syncService: SyncService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.setLastSync();
|
||||
}
|
||||
async ngOnInit() {
|
||||
await this.setLastSync();
|
||||
}
|
||||
|
||||
async sync() {
|
||||
this.syncPromise = this.syncService.fullSync(true);
|
||||
const success = await this.syncPromise;
|
||||
if (success) {
|
||||
await this.setLastSync();
|
||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('syncingComplete'));
|
||||
} else {
|
||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('syncingFailed'));
|
||||
}
|
||||
async sync() {
|
||||
this.syncPromise = this.syncService.fullSync(true);
|
||||
const success = await this.syncPromise;
|
||||
if (success) {
|
||||
await this.setLastSync();
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("syncingComplete"));
|
||||
} else {
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("syncingFailed"));
|
||||
}
|
||||
}
|
||||
|
||||
async setLastSync() {
|
||||
const last = await this.syncService.getLastSync();
|
||||
if (last != null) {
|
||||
this.lastSync = last.toLocaleDateString() + ' ' + last.toLocaleTimeString();
|
||||
} else {
|
||||
this.lastSync = this.i18nService.t('never');
|
||||
}
|
||||
async setLastSync() {
|
||||
const last = await this.syncService.getLastSync();
|
||||
if (last != null) {
|
||||
this.lastSync = last.toLocaleDateString() + " " + last.toLocaleTimeString();
|
||||
} else {
|
||||
this.lastSync = this.i18nService.t("never");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,48 @@
|
||||
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
|
||||
{{'vaultTimeoutPolicyInEffect' | i18n : vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes}}
|
||||
{{ "vaultTimeoutPolicyInEffect" | i18n: vaultTimeoutPolicyHours:vaultTimeoutPolicyMinutes }}
|
||||
</app-callout>
|
||||
|
||||
<div [formGroup]="form">
|
||||
<div class="box-content-row last display-block" appBoxRow>
|
||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
||||
<select id="vaultTimeout" name="VaultTimeout" formControlName="vaultTimeout" class="form-control">
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row last" *ngIf="showCustom">
|
||||
<div formGroupName="custom" class="row">
|
||||
<div class="col">
|
||||
<div class="display-block" appBoxRow>
|
||||
<label for="customVaultTimeout">{{'hours' | i18n }}</label>
|
||||
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="display-block" appBoxRow>
|
||||
<label for="customVaultTimeout">{{'minutes' | i18n }}</label>
|
||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes"
|
||||
formControlName="minutes">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row last display-block" appBoxRow>
|
||||
<label for="vaultTimeout">{{ "vaultTimeout" | i18n }}</label>
|
||||
<select
|
||||
id="vaultTimeout"
|
||||
name="VaultTimeout"
|
||||
formControlName="vaultTimeout"
|
||||
class="form-control"
|
||||
>
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row last" *ngIf="showCustom">
|
||||
<div formGroupName="custom" class="row">
|
||||
<div class="col">
|
||||
<div class="display-block" appBoxRow>
|
||||
<label for="customVaultTimeout">{{ "hours" | i18n }}</label>
|
||||
<input
|
||||
id="hours"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
name="hours"
|
||||
formControlName="hours"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="display-block" appBoxRow>
|
||||
<label for="customVaultTimeout">{{ "minutes" | i18n }}</label>
|
||||
<input
|
||||
id="minutes"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
max="59"
|
||||
name="minutes"
|
||||
formControlName="minutes"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
} from '@angular/forms';
|
||||
import { Component } from "@angular/core";
|
||||
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import {
|
||||
VaultTimeoutInputComponent as VaultTimeoutInputComponentBase
|
||||
} from 'jslib-angular/components/settings/vault-timeout-input.component';
|
||||
import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "jslib-angular/components/settings/vault-timeout-input.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-timeout-input',
|
||||
templateUrl: 'vault-timeout-input.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
selector: "app-vault-timeout-input",
|
||||
templateUrl: "vault-timeout-input.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {
|
||||
}
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
<div class="tab-page">
|
||||
<router-outlet></router-outlet>
|
||||
<nav class="tabs">
|
||||
<ul>
|
||||
<li routerLinkActive="active" *ngIf="showCurrentTab">
|
||||
<a routerLink="current" appA11yTitle="{{'currentTab' | i18n}}">
|
||||
<i class="fa fa-folder fa-2x" aria-hidden="true"></i>{{'tab' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="vault" appA11yTitle="{{'myVault' | i18n}}">
|
||||
<i class="fa fa-lock fa-2x" aria-hidden="true"></i>{{'myVault' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="send" appA11yTitle="{{'send' | i18n}}">
|
||||
<i class="fa fa-paper-plane fa-2x" aria-hidden="true"></i>{{'send' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="generator" appA11yTitle="{{'passGen' | i18n}}">
|
||||
<i class="fa fa-refresh fa-2x" aria-hidden="true"></i>{{'generator' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="settings" appA11yTitle="{{'settings' | i18n}}">
|
||||
<i class="fa fa-cogs fa-2x" aria-hidden="true"></i>{{'settings' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<router-outlet></router-outlet>
|
||||
<nav class="tabs">
|
||||
<ul>
|
||||
<li routerLinkActive="active" *ngIf="showCurrentTab">
|
||||
<a routerLink="current" appA11yTitle="{{ 'currentTab' | i18n }}">
|
||||
<i class="fa fa-folder fa-2x" aria-hidden="true"></i>{{ "tab" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="vault" appA11yTitle="{{ 'myVault' | i18n }}">
|
||||
<i class="fa fa-lock fa-2x" aria-hidden="true"></i>{{ "myVault" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="send" appA11yTitle="{{ 'send' | i18n }}">
|
||||
<i class="fa fa-paper-plane fa-2x" aria-hidden="true"></i>{{ "send" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="generator" appA11yTitle="{{ 'passGen' | i18n }}">
|
||||
<i class="fa fa-refresh fa-2x" aria-hidden="true"></i>{{ "generator" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li routerLinkActive="active">
|
||||
<a routerLink="settings" appA11yTitle="{{ 'settings' | i18n }}">
|
||||
<i class="fa fa-cogs fa-2x" aria-hidden="true"></i>{{ "settings" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { PopupUtilsService } from './services/popup-utils.service';
|
||||
import { PopupUtilsService } from "./services/popup-utils.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-tabs',
|
||||
templateUrl: 'tabs.component.html',
|
||||
selector: "app-tabs",
|
||||
templateUrl: "tabs.component.html",
|
||||
})
|
||||
export class TabsComponent implements OnInit {
|
||||
showCurrentTab: boolean = true;
|
||||
showCurrentTab: boolean = true;
|
||||
|
||||
constructor(private popupUtilsService: PopupUtilsService) { }
|
||||
constructor(private popupUtilsService: PopupUtilsService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.showCurrentTab = !this.popupUtilsService.inPopout(window);
|
||||
}
|
||||
ngOnInit() {
|
||||
this.showCurrentTab = !this.popupUtilsService.inPopout(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,121 @@
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
{{'customFields' | i18n}}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<!-- Current custom fields -->
|
||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||
<div class="box-content-row box-content-row-multi box-draggable-row" appBoxRow cdkDrag
|
||||
*ngFor="let f of cipher.fields; let i = index; trackBy:trackByFunction"
|
||||
[ngClass]="{'box-content-row-checkbox': f.type === fieldType.Boolean}">
|
||||
<button type="button" appStopClick (click)="removeField(f)" appA11yTitle="{{'remove' | i18n}}">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<label for="fieldName{{i}}" class="sr-only">{{'name' | i18n}}</label>
|
||||
<label for="fieldValue{{i}}" class="sr-only">{{'value' | i18n}}</label>
|
||||
<div class="row-main">
|
||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name" class="row-label"
|
||||
placeholder="{{'name' | i18n}}" appInputVerbatim>
|
||||
<!-- Text -->
|
||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}" appInputVerbatim>
|
||||
<!-- Hidden -->
|
||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}" name="Field.Value{{i}}"
|
||||
[(ngModel)]="f.value" class="monospaced" appInputVerbatim *ngIf="f.type === fieldType.Hidden"
|
||||
placeholder="{{'value' | i18n}}" [disabled]="!cipher.viewPassword && !f.newField">
|
||||
<!-- Linked -->
|
||||
<select id="fieldValue{{i}}" name="Field.Value{{i}}" [(ngModel)]="f.linkedId"
|
||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null">
|
||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Boolean -->
|
||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Boolean" appTrueFalseValue trueValue="true" falseValue="false">
|
||||
<div class="action-buttons" *ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
(click)="toggleFieldValue(f)" [attr.aria-pressed]="f.showValue">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="drag-handle" appA11yTitle="{{'dragToSort' | i18n}}" cdkDragHandle>
|
||||
<i class="fa fa-bars" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="box-header">
|
||||
{{ "customFields" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content">
|
||||
<!-- Current custom fields -->
|
||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||
<div
|
||||
class="box-content-row box-content-row-multi box-draggable-row"
|
||||
appBoxRow
|
||||
cdkDrag
|
||||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="removeField(f)"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<label for="fieldName{{ i }}" class="sr-only">{{ "name" | i18n }}</label>
|
||||
<label for="fieldValue{{ i }}" class="sr-only">{{ "value" | i18n }}</label>
|
||||
<div class="row-main">
|
||||
<input
|
||||
id="fieldName{{ i }}"
|
||||
type="text"
|
||||
name="Field.Name{{ i }}"
|
||||
[(ngModel)]="f.name"
|
||||
class="row-label"
|
||||
placeholder="{{ 'name' | i18n }}"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<!-- Text -->
|
||||
<input
|
||||
id="fieldValue{{ i }}"
|
||||
type="text"
|
||||
name="Field.Value{{ i }}"
|
||||
[(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text"
|
||||
placeholder="{{ 'value' | i18n }}"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<!-- Hidden -->
|
||||
<input
|
||||
id="fieldValue{{ i }}"
|
||||
type="{{ f.showValue ? 'text' : 'password' }}"
|
||||
name="Field.Value{{ i }}"
|
||||
[(ngModel)]="f.value"
|
||||
class="monospaced"
|
||||
appInputVerbatim
|
||||
*ngIf="f.type === fieldType.Hidden"
|
||||
placeholder="{{ 'value' | i18n }}"
|
||||
[disabled]="!cipher.viewPassword && !f.newField"
|
||||
/>
|
||||
<!-- Linked -->
|
||||
<select
|
||||
id="fieldValue{{ i }}"
|
||||
name="Field.Value{{ i }}"
|
||||
[(ngModel)]="f.linkedId"
|
||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
||||
>
|
||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Add new custom field -->
|
||||
<div class="box-content-row box-content-row-newmulti" appBoxRow>
|
||||
<button type="button" appStopClick (click)="addField()">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{'newCustomField' | i18n}}
|
||||
</button>
|
||||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
<option *ngIf="cipher.linkedFieldOptions != null" [ngValue]="addFieldLinkedTypeOption.value">
|
||||
{{addFieldLinkedTypeOption.name}}
|
||||
</option>
|
||||
</select>
|
||||
<!-- Boolean -->
|
||||
<input
|
||||
id="fieldValue{{ i }}"
|
||||
name="Field.Value{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Boolean"
|
||||
appTrueFalseValue
|
||||
trueValue="true"
|
||||
falseValue="false"
|
||||
/>
|
||||
<div
|
||||
class="action-buttons"
|
||||
*ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="row-btn"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleFieldValue(f)"
|
||||
[attr.aria-pressed]="f.showValue"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="drag-handle" appA11yTitle="{{ 'dragToSort' | i18n }}" cdkDragHandle>
|
||||
<i class="fa fa-bars" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add new custom field -->
|
||||
<div class="box-content-row box-content-row-newmulti" appBoxRow>
|
||||
<button type="button" appStopClick (click)="addField()">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ "newCustomField" | i18n }}
|
||||
</button>
|
||||
<label for="addFieldType" class="sr-only">{{ "type" | i18n }}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
<option
|
||||
*ngIf="cipher.linkedFieldOptions != null"
|
||||
[ngValue]="addFieldLinkedTypeOption.value"
|
||||
>
|
||||
{{ addFieldLinkedTypeOption.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent
|
||||
} from 'jslib-angular/components/add-edit-custom-fields.component';
|
||||
import { AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent } from "jslib-angular/components/add-edit-custom-fields.component";
|
||||
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-add-edit-custom-fields',
|
||||
templateUrl: 'add-edit-custom-fields.component.html',
|
||||
selector: "app-vault-add-edit-custom-fields",
|
||||
templateUrl: "add-edit-custom-fields.component.html",
|
||||
})
|
||||
export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsComponent {
|
||||
constructor(i18nService: I18nService, eventService: EventService) {
|
||||
super(i18nService, eventService);
|
||||
}
|
||||
constructor(i18nService: I18nService, eventService: EventService) {
|
||||
super(i18nService, eventService);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,194 +1,229 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from 'rxjs/operators';
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
import { BrowserApi } from "../../browser/browserApi";
|
||||
|
||||
import { AuditService } from 'jslib-common/abstractions/audit.service';
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { AuditService } from "jslib-common/abstractions/audit.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { UserService } from "jslib-common/abstractions/user.service";
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { ConstantsService } from "jslib-common/services/constants.service";
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
import { PopupUtilsService } from "../services/popup-utils.service";
|
||||
|
||||
import { LoginUriView } from 'jslib-common/models/view/loginUriView';
|
||||
import { LoginUriView } from "jslib-common/models/view/loginUriView";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/add-edit.component';
|
||||
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/add-edit.component";
|
||||
|
||||
import { CipherType } from 'jslib-common/enums/cipherType';
|
||||
import { CipherType } from "jslib-common/enums/cipherType";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-add-edit',
|
||||
templateUrl: 'add-edit.component.html',
|
||||
selector: "app-vault-add-edit",
|
||||
templateUrl: "add-edit.component.html",
|
||||
})
|
||||
export class AddEditComponent extends BaseAddEditComponent {
|
||||
currentUris: string[];
|
||||
showAttachments = true;
|
||||
openAttachmentsInPopup: boolean;
|
||||
showAutoFillOnPageLoadOptions: boolean;
|
||||
currentUris: string[];
|
||||
showAttachments = true;
|
||||
openAttachmentsInPopup: boolean;
|
||||
showAutoFillOnPageLoadOptions: boolean;
|
||||
|
||||
constructor(cipherService: CipherService, folderService: FolderService,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService, stateService: StateService,
|
||||
userService: UserService, collectionService: CollectionService,
|
||||
messagingService: MessagingService, private route: ActivatedRoute,
|
||||
private router: Router, private location: Location,
|
||||
eventService: EventService, policyService: PolicyService,
|
||||
private popupUtilsService: PopupUtilsService, private storageService: StorageService,
|
||||
logService: LogService, passwordRepromptService: PasswordRepromptService) {
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
||||
userService, collectionService, messagingService, eventService, policyService, passwordRepromptService,
|
||||
logService);
|
||||
}
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
folderService: FolderService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
userService: UserService,
|
||||
collectionService: CollectionService,
|
||||
messagingService: MessagingService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
eventService: EventService,
|
||||
policyService: PolicyService,
|
||||
private popupUtilsService: PopupUtilsService,
|
||||
private storageService: StorageService,
|
||||
logService: LogService,
|
||||
passwordRepromptService: PasswordRepromptService
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
folderService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
userService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
eventService,
|
||||
policyService,
|
||||
passwordRepromptService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
|
||||
this.route.queryParams.pipe(first()).subscribe(async params => {
|
||||
if (params.cipherId) {
|
||||
this.cipherId = params.cipherId;
|
||||
}
|
||||
if (params.folderId) {
|
||||
this.folderId = params.folderId;
|
||||
}
|
||||
if (params.collectionId) {
|
||||
const collection = this.writeableCollections.find(c => c.id === params.collectionId);
|
||||
if (collection != null) {
|
||||
this.collectionIds = [collection.id];
|
||||
this.organizationId = collection.organizationId;
|
||||
}
|
||||
}
|
||||
if (params.type) {
|
||||
const type = parseInt(params.type, null);
|
||||
this.type = type;
|
||||
}
|
||||
this.editMode = !params.cipherId;
|
||||
|
||||
if (params.cloneMode != null) {
|
||||
this.cloneMode = params.cloneMode === 'true';
|
||||
}
|
||||
await this.load();
|
||||
|
||||
if (!this.editMode || this.cloneMode) {
|
||||
if (!this.popupUtilsService.inPopout(window) && params.name &&
|
||||
(this.cipher.name == null || this.cipher.name === '')) {
|
||||
this.cipher.name = params.name;
|
||||
}
|
||||
if (!this.popupUtilsService.inPopout(window) && params.uri &&
|
||||
(this.cipher.login.uris[0].uri == null || this.cipher.login.uris[0].uri === '')) {
|
||||
this.cipher.login.uris[0].uri = params.uri;
|
||||
}
|
||||
}
|
||||
|
||||
this.openAttachmentsInPopup = this.popupUtilsService.inPopup(window);
|
||||
});
|
||||
|
||||
if (!this.editMode) {
|
||||
const tabs = await BrowserApi.tabsQuery({ windowType: 'normal' });
|
||||
this.currentUris = tabs == null ? null :
|
||||
tabs.filter(tab => tab.url != null && tab.url !== '').map(tab => tab.url);
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
if (params.cipherId) {
|
||||
this.cipherId = params.cipherId;
|
||||
}
|
||||
if (params.folderId) {
|
||||
this.folderId = params.folderId;
|
||||
}
|
||||
if (params.collectionId) {
|
||||
const collection = this.writeableCollections.find((c) => c.id === params.collectionId);
|
||||
if (collection != null) {
|
||||
this.collectionIds = [collection.id];
|
||||
this.organizationId = collection.organizationId;
|
||||
}
|
||||
}
|
||||
if (params.type) {
|
||||
const type = parseInt(params.type, null);
|
||||
this.type = type;
|
||||
}
|
||||
this.editMode = !params.cipherId;
|
||||
|
||||
window.setTimeout(() => {
|
||||
if (!this.editMode) {
|
||||
if (this.cipher.name != null && this.cipher.name !== '') {
|
||||
document.getElementById('loginUsername').focus();
|
||||
} else {
|
||||
document.getElementById('name').focus();
|
||||
}
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
if (params.cloneMode != null) {
|
||||
this.cloneMode = params.cloneMode === "true";
|
||||
}
|
||||
await this.load();
|
||||
|
||||
async load() {
|
||||
await super.load();
|
||||
this.showAutoFillOnPageLoadOptions = this.cipher.type === CipherType.Login &&
|
||||
await this.storageService.get<boolean>(ConstantsService.enableAutoFillOnPageLoadKey);
|
||||
}
|
||||
|
||||
async submit(): Promise<boolean> {
|
||||
if (await super.submit()) {
|
||||
if (this.cloneMode) {
|
||||
this.router.navigate(['/tabs/vault']);
|
||||
} else {
|
||||
this.location.back();
|
||||
}
|
||||
return true;
|
||||
if (!this.editMode || this.cloneMode) {
|
||||
if (
|
||||
!this.popupUtilsService.inPopout(window) &&
|
||||
params.name &&
|
||||
(this.cipher.name == null || this.cipher.name === "")
|
||||
) {
|
||||
this.cipher.name = params.name;
|
||||
}
|
||||
if (
|
||||
!this.popupUtilsService.inPopout(window) &&
|
||||
params.uri &&
|
||||
(this.cipher.login.uris[0].uri == null || this.cipher.login.uris[0].uri === "")
|
||||
) {
|
||||
this.cipher.login.uris[0].uri = params.uri;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
this.openAttachmentsInPopup = this.popupUtilsService.inPopup(window);
|
||||
});
|
||||
|
||||
if (!this.editMode) {
|
||||
const tabs = await BrowserApi.tabsQuery({ windowType: "normal" });
|
||||
this.currentUris =
|
||||
tabs == null
|
||||
? null
|
||||
: tabs.filter((tab) => tab.url != null && tab.url !== "").map((tab) => tab.url);
|
||||
}
|
||||
|
||||
attachments() {
|
||||
super.attachments();
|
||||
|
||||
if (this.openAttachmentsInPopup) {
|
||||
const destinationUrl = this.router.createUrlTree(['/attachments'], { queryParams: { cipherId: this.cipher.id } }).toString();
|
||||
const currentBaseUrl = window.location.href.replace(this.router.url, '');
|
||||
this.popupUtilsService.popOut(window, currentBaseUrl + destinationUrl);
|
||||
window.setTimeout(() => {
|
||||
if (!this.editMode) {
|
||||
if (this.cipher.name != null && this.cipher.name !== "") {
|
||||
document.getElementById("loginUsername").focus();
|
||||
} else {
|
||||
this.router.navigate(['/attachments'], { queryParams: { cipherId: this.cipher.id } });
|
||||
document.getElementById("name").focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
async load() {
|
||||
await super.load();
|
||||
this.showAutoFillOnPageLoadOptions =
|
||||
this.cipher.type === CipherType.Login &&
|
||||
(await this.storageService.get<boolean>(ConstantsService.enableAutoFillOnPageLoadKey));
|
||||
}
|
||||
|
||||
editCollections() {
|
||||
super.editCollections();
|
||||
if (this.cipher.organizationId != null) {
|
||||
this.router.navigate(['/collections'], { queryParams: { cipherId: this.cipher.id } });
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
super.cancel();
|
||||
async submit(): Promise<boolean> {
|
||||
if (await super.submit()) {
|
||||
if (this.cloneMode) {
|
||||
this.router.navigate(["/tabs/vault"]);
|
||||
} else {
|
||||
this.location.back();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async generatePassword(): Promise<boolean> {
|
||||
const confirmed = await super.generatePassword();
|
||||
if (confirmed) {
|
||||
this.stateService.save('addEditCipherInfo', {
|
||||
cipher: this.cipher,
|
||||
collectionIds: this.collections == null ? [] :
|
||||
this.collections.filter(c => (c as any).checked).map(c => c.id),
|
||||
});
|
||||
this.router.navigate(['generator']);
|
||||
}
|
||||
return confirmed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async delete(): Promise<boolean> {
|
||||
const confirmed = await super.delete();
|
||||
if (confirmed) {
|
||||
this.router.navigate(['/tabs/vault']);
|
||||
}
|
||||
return confirmed;
|
||||
}
|
||||
attachments() {
|
||||
super.attachments();
|
||||
|
||||
toggleUriInput(uri: LoginUriView) {
|
||||
const u = (uri as any);
|
||||
u.showCurrentUris = !u.showCurrentUris;
|
||||
if (this.openAttachmentsInPopup) {
|
||||
const destinationUrl = this.router
|
||||
.createUrlTree(["/attachments"], { queryParams: { cipherId: this.cipher.id } })
|
||||
.toString();
|
||||
const currentBaseUrl = window.location.href.replace(this.router.url, "");
|
||||
this.popupUtilsService.popOut(window, currentBaseUrl + destinationUrl);
|
||||
} else {
|
||||
this.router.navigate(["/attachments"], { queryParams: { cipherId: this.cipher.id } });
|
||||
}
|
||||
}
|
||||
|
||||
allowOwnershipOptions(): boolean {
|
||||
return (!this.editMode || this.cloneMode) && this.ownershipOptions
|
||||
&& (this.ownershipOptions.length > 1 || !this.allowPersonal);
|
||||
editCollections() {
|
||||
super.editCollections();
|
||||
if (this.cipher.organizationId != null) {
|
||||
this.router.navigate(["/collections"], { queryParams: { cipherId: this.cipher.id } });
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
super.cancel();
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
async generatePassword(): Promise<boolean> {
|
||||
const confirmed = await super.generatePassword();
|
||||
if (confirmed) {
|
||||
this.stateService.save("addEditCipherInfo", {
|
||||
cipher: this.cipher,
|
||||
collectionIds:
|
||||
this.collections == null
|
||||
? []
|
||||
: this.collections.filter((c) => (c as any).checked).map((c) => c.id),
|
||||
});
|
||||
this.router.navigate(["generator"]);
|
||||
}
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
async delete(): Promise<boolean> {
|
||||
const confirmed = await super.delete();
|
||||
if (confirmed) {
|
||||
this.router.navigate(["/tabs/vault"]);
|
||||
}
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
toggleUriInput(uri: LoginUriView) {
|
||||
const u = uri as any;
|
||||
u.showCurrentUris = !u.showCurrentUris;
|
||||
}
|
||||
|
||||
allowOwnershipOptions(): boolean {
|
||||
return (
|
||||
(!this.editMode || this.cloneMode) &&
|
||||
this.ownershipOptions &&
|
||||
(this.ownershipOptions.length > 1 || !this.allowPersonal)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user