mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-26 12:25:20 +01:00
Apply Prettier (#1202)
This commit is contained in:
parent
b4df834b16
commit
521feae535
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,4 +1,5 @@
|
|||||||
## Type of change
|
## Type of change
|
||||||
|
|
||||||
- [ ] Bug fix
|
- [ ] Bug fix
|
||||||
- [ ] New feature development
|
- [ ] New feature development
|
||||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||||
@ -6,27 +7,26 @@
|
|||||||
- [ ] Other
|
- [ ] Other
|
||||||
|
|
||||||
## Objective
|
## Objective
|
||||||
|
|
||||||
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
|
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Code changes
|
## Code changes
|
||||||
|
|
||||||
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
|
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
|
||||||
<!--Also refer to any related changes or PRs in other repositories-->
|
<!--Also refer to any related changes or PRs in other repositories-->
|
||||||
|
|
||||||
* **file.ext:** Description of what was changed and why
|
- **file.ext:** Description of what was changed and why
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
<!--Required for any UI changes. Delete if not applicable-->
|
<!--Required for any UI changes. Delete if not applicable-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Testing requirements
|
## Testing requirements
|
||||||
|
|
||||||
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Before you submit
|
## Before you submit
|
||||||
|
|
||||||
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
|
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
|
||||||
- [ ] This change requires a **documentation update** (notify the documentation team)
|
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||||
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||||
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -128,8 +128,8 @@ jobs:
|
|||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
# - name: Run linter
|
- name: Run linter
|
||||||
# run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Build application
|
- name: Build application
|
||||||
run: npm run dist:lin
|
run: npm run dist:lin
|
||||||
@ -224,8 +224,8 @@ jobs:
|
|||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
# - name: Run linter
|
- name: Run linter
|
||||||
# run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Build & Sign (dev)
|
- name: Build & Sign (dev)
|
||||||
env:
|
env:
|
||||||
@ -477,8 +477,8 @@ jobs:
|
|||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
# - name: Run linter
|
- name: Run linter
|
||||||
# run: npm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Build application (dev)
|
- name: Build application (dev)
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
2
.github/workflows/crowndin-pull.yml
vendored
2
.github/workflows/crowndin-pull.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs: {}
|
inputs: {}
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 5'
|
- cron: "0 0 * * 5"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
crowdin-sync:
|
crowdin-sync:
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -93,7 +93,6 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
draft: true
|
draft: true
|
||||||
|
|
||||||
|
|
||||||
snap:
|
snap:
|
||||||
name: Deploy Snap
|
name: Deploy Snap
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
@ -129,7 +128,6 @@ jobs:
|
|||||||
snapcraft upload dist/bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
|
snapcraft upload dist/bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
|
||||||
snapcraft logout
|
snapcraft logout
|
||||||
|
|
||||||
|
|
||||||
choco:
|
choco:
|
||||||
name: Deploy Choco
|
name: Deploy Choco
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
|
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -10,9 +10,7 @@
|
|||||||
"windows": {
|
"windows": {
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||||
},
|
},
|
||||||
"args": [
|
"args": ["."]
|
||||||
"."
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,12 @@ Please visit our [Community Forums](https://community.bitwarden.com/) for genera
|
|||||||
|
|
||||||
Here is how you can get involved:
|
Here is how you can get involved:
|
||||||
|
|
||||||
* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
- **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
||||||
|
- **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||||
* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
- **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||||
|
- **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||||
* **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
- **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||||
|
- **Translate:** See the localization (l10n) section below
|
||||||
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
|
||||||
|
|
||||||
* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
|
||||||
|
|
||||||
* **Translate:** See the localization (l10n) section below
|
|
||||||
|
|
||||||
## Contributor Agreement
|
## Contributor Agreement
|
||||||
|
|
||||||
@ -24,9 +19,9 @@ Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/deskt
|
|||||||
|
|
||||||
## Pull Request Guidelines
|
## Pull Request Guidelines
|
||||||
|
|
||||||
* use `npm run lint` and fix any linting suggestions before submitting a pull request
|
- use `npm run lint` and fix any linting suggestions before submitting a pull request
|
||||||
* commit any pull requests against the `master` branch
|
- commit any pull requests against the `master` branch
|
||||||
* include a link to your Community Forums post
|
- include a link to your Community Forums post
|
||||||
|
|
||||||
# Localization (l10n)
|
# Localization (l10n)
|
||||||
|
|
||||||
|
@ -16,8 +16,7 @@ The Bitwarden desktop app is written using Electron and Angular. The application
|
|||||||
|
|
||||||
- [Node.js](https://nodejs.org) v16.13.1 (LTS) or greater
|
- [Node.js](https://nodejs.org) v16.13.1 (LTS) or greater
|
||||||
- NPM v8
|
- NPM v8
|
||||||
- Windows users: To compile the native node modules used in the app you will need the *Visual C++ toolset*, available through the standard Visual Studio installer. You will also need to install the *Microsoft Build Tools 2015* and *Windows 10 SDK 17134* as additional dependencies in the Visual Studio installer.
|
- Windows users: To compile the native node modules used in the app you will need the _Visual C++ toolset_, available through the standard Visual Studio installer. You will also need to install the _Microsoft Build Tools 2015_ and _Windows 10 SDK 17134_ as additional dependencies in the Visual Studio installer.
|
||||||
|
|
||||||
|
|
||||||
**Run the app**
|
**Run the app**
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ npm run electron
|
|||||||
Native Messaging (communication with the browser extension) works by having the browser start a lightweight proxy application baked into our desktop binary. To setup an environment which allows
|
Native Messaging (communication with the browser extension) works by having the browser start a lightweight proxy application baked into our desktop binary. To setup an environment which allows
|
||||||
for easy debugging you will need to build the application for distribution, i.e. `npm run dist:<platform>`, start the dist version and enable desktop integration. This will write some manifests
|
for easy debugging you will need to build the application for distribution, i.e. `npm run dist:<platform>`, start the dist version and enable desktop integration. This will write some manifests
|
||||||
to disk, Consult the [native manifests](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location) documentation for more details of the manifest
|
to disk, Consult the [native manifests](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location) documentation for more details of the manifest
|
||||||
format, and the exact locations for the different platforms. *Note* that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
format, and the exact locations for the different platforms. _Note_ that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
||||||
|
|
||||||
The generated manifests are pre-configured with the production ID for the browser extensions. In order to use them with the development builds, the browser extension ID of the development build
|
The generated manifests are pre-configured with the production ID for the browser extensions. In order to use them with the development builds, the browser extension ID of the development build
|
||||||
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"symlink:win": "rm -rf ./jslib && cmd /c mklink /J .\\jslib ..\\jslib",
|
"symlink:win": "rm -rf ./jslib && cmd /c mklink /J .\\jslib ..\\jslib",
|
||||||
"symlink:mac": "npm run symlink:lin",
|
"symlink:mac": "npm run symlink:lin",
|
||||||
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
|
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
|
||||||
"lint": "tslint 'src/**/*.ts'",
|
"lint": "tslint 'src/**/*.ts' && prettier --check .",
|
||||||
"lint:fix": "tslint 'src/**/*.ts' --fix",
|
"lint:fix": "tslint 'src/**/*.ts' --fix",
|
||||||
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
|
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
|
||||||
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
|
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
|
||||||
|
@ -1,33 +1,40 @@
|
|||||||
require('dotenv').config();
|
require("dotenv").config();
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const fse = require('fs-extra');
|
const fse = require("fs-extra");
|
||||||
const { notarize } = require('electron-notarize');
|
const { notarize } = require("electron-notarize");
|
||||||
const { deepAssign } = require('builder-util');
|
const { deepAssign } = require("builder-util");
|
||||||
|
|
||||||
exports.default = run;
|
exports.default = run;
|
||||||
|
|
||||||
async function run(context) {
|
async function run(context) {
|
||||||
console.log('## After sign');
|
console.log("## After sign");
|
||||||
// console.log(context);
|
// console.log(context);
|
||||||
|
|
||||||
const appName = context.packager.appInfo.productFilename;
|
const appName = context.packager.appInfo.productFilename;
|
||||||
const appPath = `${context.appOutDir}/${appName}.app`;
|
const appPath = `${context.appOutDir}/${appName}.app`;
|
||||||
const macBuild = context.electronPlatformName === 'darwin';
|
const macBuild = context.electronPlatformName === "darwin";
|
||||||
const copyPlugIn = ['darwin', 'mas'].includes(context.electronPlatformName);
|
const copyPlugIn = ["darwin", "mas"].includes(context.electronPlatformName);
|
||||||
|
|
||||||
if (copyPlugIn) {
|
if (copyPlugIn) {
|
||||||
// Copy Safari plugin to work-around https://github.com/electron-userland/electron-builder/issues/5552
|
// Copy Safari plugin to work-around https://github.com/electron-userland/electron-builder/issues/5552
|
||||||
const plugIn = path.join(__dirname, '../PlugIns');
|
const plugIn = path.join(__dirname, "../PlugIns");
|
||||||
if (fse.existsSync(plugIn)) {
|
if (fse.existsSync(plugIn)) {
|
||||||
fse.mkdirSync(path.join(appPath, 'Contents/PlugIns'));
|
fse.mkdirSync(path.join(appPath, "Contents/PlugIns"));
|
||||||
fse.copySync(path.join(plugIn, 'safari.appex'), path.join(appPath, 'Contents/PlugIns/safari.appex'));
|
fse.copySync(
|
||||||
|
path.join(plugIn, "safari.appex"),
|
||||||
|
path.join(appPath, "Contents/PlugIns/safari.appex")
|
||||||
|
);
|
||||||
|
|
||||||
// Resign to sign safari extension
|
// Resign to sign safari extension
|
||||||
if (context.electronPlatformName === 'mas') {
|
if (context.electronPlatformName === "mas") {
|
||||||
const masBuildOptions = deepAssign({}, context.packager.platformSpecificBuildOptions, context.packager.config.mas);
|
const masBuildOptions = deepAssign(
|
||||||
if (context.targets.some(e => e.name === 'mas-dev')) {
|
{},
|
||||||
|
context.packager.platformSpecificBuildOptions,
|
||||||
|
context.packager.config.mas
|
||||||
|
);
|
||||||
|
if (context.targets.some((e) => e.name === "mas-dev")) {
|
||||||
deepAssign(masBuildOptions, {
|
deepAssign(masBuildOptions, {
|
||||||
type: 'development',
|
type: "development",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (context.packager.packagerOptions.prepackaged == null) {
|
if (context.packager.packagerOptions.prepackaged == null) {
|
||||||
@ -40,11 +47,11 @@ async function run(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (macBuild) {
|
if (macBuild) {
|
||||||
console.log('### Notarizing ' + appPath);
|
console.log("### Notarizing " + appPath);
|
||||||
const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID;
|
const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID;
|
||||||
const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`;
|
const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`;
|
||||||
return await notarize({
|
return await notarize({
|
||||||
appBundleId: 'com.bitwarden.desktop',
|
appBundleId: "com.bitwarden.desktop",
|
||||||
appPath: appPath,
|
appPath: appPath,
|
||||||
appleId: appleId,
|
appleId: appleId,
|
||||||
appleIdPassword: appleIdPassword,
|
appleIdPassword: appleIdPassword,
|
||||||
|
9
sign.js
9
sign.js
@ -1,9 +1,6 @@
|
|||||||
exports.default = async function (configuration) {
|
exports.default = async function (configuration) {
|
||||||
if (
|
if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && configuration.path.slice(-4) == ".exe") {
|
||||||
parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 &&
|
console.log(`[*] Signing file: ${configuration.path}`);
|
||||||
configuration.path.slice(-4) == ".exe"
|
|
||||||
) {
|
|
||||||
console.log(`[*] Signing file: ${configuration.path}`)
|
|
||||||
require("child_process").execSync(
|
require("child_process").execSync(
|
||||||
`azuresigntool sign -v ` +
|
`azuresigntool sign -v ` +
|
||||||
`-kvu ${process.env.SIGNING_VAULT_URL} ` +
|
`-kvu ${process.env.SIGNING_VAULT_URL} ` +
|
||||||
@ -16,7 +13,7 @@ exports.default = async function(configuration) {
|
|||||||
`-tr http://timestamp.digicert.com ` +
|
`-tr http://timestamp.digicert.com ` +
|
||||||
`${configuration.path}`,
|
`${configuration.path}`,
|
||||||
{
|
{
|
||||||
stdio: "inherit"
|
stdio: "inherit",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,54 +4,85 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'selfHostedEnvironment' | i18n}}
|
{{ "selfHostedEnvironment" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="baseUrl">{{'baseUrl' | i18n}}</label>
|
<label for="baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||||
<input id="baseUrl" type="text" name="BaseUrl" [(ngModel)]="baseUrl"
|
<input
|
||||||
placeholder="{{'ex' | i18n}} https://bitwarden.company.com" appInputVerbatim>
|
id="baseUrl"
|
||||||
|
type="text"
|
||||||
|
name="BaseUrl"
|
||||||
|
[(ngModel)]="baseUrl"
|
||||||
|
placeholder="{{ 'ex' | i18n }} https://bitwarden.company.com"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'selfHostedEnvironmentFooter' | i18n}}
|
{{ "selfHostedEnvironmentFooter" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<button type="button" (click)="toggleCustom()" appA11yTitle="{{'toggleVisibility' | i18n}}">
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="toggleCustom()"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-plus-square-o" [hidden]="showCustom" aria-hidden="true"></i>
|
<i class="fa fa-plus-square-o" [hidden]="showCustom" aria-hidden="true"></i>
|
||||||
<i class="fa fa-minus-square-o" [hidden]="!showCustom" aria-hidden="true"></i>
|
<i class="fa fa-minus-square-o" [hidden]="!showCustom" aria-hidden="true"></i>
|
||||||
{{'customEnvironment' | i18n}}
|
{{ "customEnvironment" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" [hidden]="!showCustom">
|
<div class="box-content" [hidden]="!showCustom">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="webVaultUrl">{{'webVaultUrl' | i18n}}</label>
|
<label for="webVaultUrl">{{ "webVaultUrl" | i18n }}</label>
|
||||||
<input id="webVaultUrl" type="text" name="WebVaultUrl" [(ngModel)]="webVaultUrl"
|
<input
|
||||||
appInputVerbatim>
|
id="webVaultUrl"
|
||||||
|
type="text"
|
||||||
|
name="WebVaultUrl"
|
||||||
|
[(ngModel)]="webVaultUrl"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="apiUrl">{{'apiUrl' | i18n}}</label>
|
<label for="apiUrl">{{ "apiUrl" | i18n }}</label>
|
||||||
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl" appInputVerbatim>
|
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl" appInputVerbatim />
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="identityUrl">{{'identityUrl' | i18n}}</label>
|
<label for="identityUrl">{{ "identityUrl" | i18n }}</label>
|
||||||
<input id="identityUrl" type="text" name="IdentityUrl" [(ngModel)]="identityUrl"
|
<input
|
||||||
appInputVerbatim>
|
id="identityUrl"
|
||||||
|
type="text"
|
||||||
|
name="IdentityUrl"
|
||||||
|
[(ngModel)]="identityUrl"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="notificationsUrl">{{'notificationsUrl' | i18n}}</label>
|
<label for="notificationsUrl">{{ "notificationsUrl" | i18n }}</label>
|
||||||
<input id="notificationsUrl" type="text" name="NotificationsUrl"
|
<input
|
||||||
[(ngModel)]="notificationsUrl" appInputVerbatim>
|
id="notificationsUrl"
|
||||||
|
type="text"
|
||||||
|
name="NotificationsUrl"
|
||||||
|
[(ngModel)]="notificationsUrl"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="iconsUrl">{{'iconsUrl' | i18n}}</label>
|
<label for="iconsUrl">{{ "iconsUrl" | i18n }}</label>
|
||||||
<input id="iconsUrl" type="text" name="IconsUrl" [(ngModel)]="iconsUrl" appInputVerbatim>
|
<input
|
||||||
|
id="iconsUrl"
|
||||||
|
type="text"
|
||||||
|
name="IconsUrl"
|
||||||
|
[(ngModel)]="iconsUrl"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer" [hidden]="!showCustom">
|
<div class="box-footer" [hidden]="!showCustom">
|
||||||
{{'customEnvironmentFooter' | i18n}}
|
{{ "customEnvironmentFooter" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -59,7 +90,7 @@
|
|||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
|
<button appBlurClick type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
|
||||||
<i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.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({
|
@Component({
|
||||||
selector: 'app-environment',
|
selector: "app-environment",
|
||||||
templateUrl: 'environment.component.html',
|
templateUrl: "environment.component.html",
|
||||||
})
|
})
|
||||||
export class EnvironmentComponent extends BaseEnvironmentComponent {
|
export class EnvironmentComponent extends BaseEnvironmentComponent {
|
||||||
constructor(platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
|
constructor(
|
||||||
i18nService: I18nService) {
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
environmentService: EnvironmentService,
|
||||||
|
i18nService: I18nService
|
||||||
|
) {
|
||||||
super(platformUtilsService, environmentService, i18nService);
|
super(platformUtilsService, environmentService, i18nService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
<form id="hint-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
<form id="hint-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>{{'passwordHint' | i18n}}</h1>
|
<h1>{{ "passwordHint" | i18n }}</h1>
|
||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required appAutofocus
|
<input
|
||||||
appInputVerbatim>
|
id="email"
|
||||||
|
type="text"
|
||||||
|
name="Email"
|
||||||
|
[(ngModel)]="email"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'enterEmailToGetHint' | i18n}}
|
{{ "enterEmailToGetHint" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { Router } from '@angular/router';
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.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({
|
@Component({
|
||||||
selector: 'app-hint',
|
selector: "app-hint",
|
||||||
templateUrl: 'hint.component.html',
|
templateUrl: "hint.component.html",
|
||||||
})
|
})
|
||||||
export class HintComponent extends BaseHintComponent {
|
export class HintComponent extends BaseHintComponent {
|
||||||
constructor(router: Router, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
i18nService: I18nService, apiService: ApiService, logService: LogService) {
|
router: Router,
|
||||||
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
i18nService: I18nService,
|
||||||
|
apiService: ApiService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,75 @@
|
|||||||
<form id="lock-page" (ngSubmit)="submit()">
|
<form id="lock-page" (ngSubmit)="submit()">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p aria-hidden="true"><i class="fa fa-lock fa-4x text-muted"></i></p>
|
<p aria-hidden="true"><i class="fa fa-lock fa-4x text-muted"></i></p>
|
||||||
<p>{{'yourVaultIsLocked' | i18n}}</p>
|
<p>{{ "yourVaultIsLocked" | i18n }}</p>
|
||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||||
<div class="row-main" *ngIf="pinLock">
|
<div class="row-main" *ngIf="pinLock">
|
||||||
<label for="pin">{{'pin' | i18n}}</label>
|
<label for="pin">{{ "pin" | i18n }}</label>
|
||||||
<input id="pin" type="{{showPassword ? 'text' : 'password'}}" name="PIN" class="monospaced"
|
<input
|
||||||
[(ngModel)]="pin" required appInputVerbatim>
|
id="pin"
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="PIN"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="pin"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-main" *ngIf="!pinLock">
|
<div class="row-main" *ngIf="!pinLock">
|
||||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
<input
|
||||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
id="masterPassword"
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPassword"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPassword"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'loggedInAsOn' | i18n : email : webVaultHostname}}
|
{{ "loggedInAsOn" | i18n: email:webVaultHostname }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons with-rows">
|
<div class="buttons with-rows">
|
||||||
<div class="buttons-row" *ngIf="supportsBiometric && biometricLock">
|
<div class="buttons-row" *ngIf="supportsBiometric && biometricLock">
|
||||||
<button type="button" class="btn block" [ngClass]="{'primary font-weight-bold': hideInput}"
|
<button
|
||||||
appBlurClick (click)="unlockBiometric()">
|
type="button"
|
||||||
|
class="btn block"
|
||||||
|
[ngClass]="{ 'primary font-weight-bold': hideInput }"
|
||||||
|
appBlurClick
|
||||||
|
(click)="unlockBiometric()"
|
||||||
|
>
|
||||||
{{ biometricText | i18n }}
|
{{ biometricText | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons-row">
|
<div class="buttons-row">
|
||||||
<button type="submit" class="btn primary block" appBlurClick *ngIf="!hideInput">
|
<button type="submit" class="btn primary block" appBlurClick *ngIf="!hideInput">
|
||||||
<i class="fa fa-unlock-alt" aria-hidden="true"></i> <b>{{'unlock' | i18n}}</b>
|
<i class="fa fa-unlock-alt" aria-hidden="true"></i> <b>{{ "unlock" | i18n }}</b>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn block" appBlurClick (click)="logOut()">
|
<button type="button" class="btn block" appBlurClick (click)="logOut()">
|
||||||
{{'logOut' | i18n}}
|
{{ "logOut" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,57 +1,70 @@
|
|||||||
import {
|
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||||
Component,
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
NgZone,
|
import { ipcRenderer } from "electron";
|
||||||
OnDestroy,
|
|
||||||
} from '@angular/core';
|
|
||||||
import {
|
|
||||||
ActivatedRoute,
|
|
||||||
Router,
|
|
||||||
} from '@angular/router';
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.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";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'LockComponent';
|
const BroadcasterSubscriptionId = "LockComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-lock',
|
selector: "app-lock",
|
||||||
templateUrl: 'lock.component.html',
|
templateUrl: "lock.component.html",
|
||||||
})
|
})
|
||||||
export class LockComponent extends BaseLockComponent implements OnDestroy {
|
export class LockComponent extends BaseLockComponent implements OnDestroy {
|
||||||
private deferFocus: boolean = null;
|
private deferFocus: boolean = null;
|
||||||
|
|
||||||
constructor(router: Router, i18nService: I18nService,
|
constructor(
|
||||||
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
|
router: Router,
|
||||||
cryptoService: CryptoService, vaultTimeoutService: VaultTimeoutService,
|
i18nService: I18nService,
|
||||||
environmentService: EnvironmentService, stateService: StateService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
apiService: ApiService, private route: ActivatedRoute,
|
messagingService: MessagingService,
|
||||||
private broadcasterService: BroadcasterService, ngZone: NgZone,
|
cryptoService: CryptoService,
|
||||||
logService: LogService, keyConnectorService: KeyConnectorService) {
|
vaultTimeoutService: VaultTimeoutService,
|
||||||
super(router, i18nService, platformUtilsService, messagingService, cryptoService,
|
environmentService: EnvironmentService,
|
||||||
vaultTimeoutService, environmentService, stateService, apiService, logService,
|
stateService: StateService,
|
||||||
keyConnectorService, ngZone);
|
apiService: ApiService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
ngZone: NgZone,
|
||||||
|
logService: LogService,
|
||||||
|
keyConnectorService: KeyConnectorService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
router,
|
||||||
|
i18nService,
|
||||||
|
platformUtilsService,
|
||||||
|
messagingService,
|
||||||
|
cryptoService,
|
||||||
|
vaultTimeoutService,
|
||||||
|
environmentService,
|
||||||
|
stateService,
|
||||||
|
apiService,
|
||||||
|
logService,
|
||||||
|
keyConnectorService,
|
||||||
|
ngZone
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
const autoPromptBiometric = !await this.stateService.getNoAutoPromptBiometrics();
|
const autoPromptBiometric = !(await this.stateService.getNoAutoPromptBiometrics());
|
||||||
|
|
||||||
this.route.queryParams.subscribe(params => {
|
this.route.queryParams.subscribe((params) => {
|
||||||
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) {
|
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
if (await ipcRenderer.invoke('windowVisible')) {
|
if (await ipcRenderer.invoke("windowVisible")) {
|
||||||
this.unlockBiometric();
|
this.unlockBiometric();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -60,10 +73,10 @@ export class LockComponent extends BaseLockComponent implements OnDestroy {
|
|||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'windowHidden':
|
case "windowHidden":
|
||||||
this.onWindowHidden();
|
this.onWindowHidden();
|
||||||
break;
|
break;
|
||||||
case 'windowIsFocused':
|
case "windowIsFocused":
|
||||||
if (this.deferFocus === null) {
|
if (this.deferFocus === null) {
|
||||||
this.deferFocus = !message.windowIsFocused;
|
this.deferFocus = !message.windowIsFocused;
|
||||||
if (!this.deferFocus) {
|
if (!this.deferFocus) {
|
||||||
@ -78,7 +91,7 @@ export class LockComponent extends BaseLockComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.messagingService.send('getWindowIsFocused');
|
this.messagingService.send("getWindowIsFocused");
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -90,6 +103,6 @@ export class LockComponent extends BaseLockComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private focusInput() {
|
private focusInput() {
|
||||||
document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus();
|
document.getElementById(this.pinLock ? "pin" : "masterPassword").focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,66 @@
|
|||||||
<div class="login-header">
|
<div class="login-header">
|
||||||
<a href="#" appStopClick (click)="settings()" class="environment-urls-settings-icon" attr.aria-label="{{'settings' | i18n}}">
|
<a
|
||||||
{{'settings' | i18n}}
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
(click)="settings()"
|
||||||
|
class="environment-urls-settings-icon"
|
||||||
|
attr.aria-label="{{ 'settings' | i18n }}"
|
||||||
|
>
|
||||||
|
{{ "settings" | i18n }}
|
||||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<form id="login-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise" attr.aria-hidden="{{showingModal}}">
|
<form
|
||||||
|
id="login-page"
|
||||||
|
#form
|
||||||
|
(ngSubmit)="submit()"
|
||||||
|
[appApiAction]="formPromise"
|
||||||
|
attr.aria-hidden="{{ showingModal }}"
|
||||||
|
>
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<img class="logo-image" alt="Bitwarden">
|
<img class="logo-image" alt="Bitwarden" />
|
||||||
<p class="lead">{{'loginOrCreateNewAccount' | i18n}}</p>
|
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required appInputVerbatim="false">
|
<input
|
||||||
|
id="email"
|
||||||
|
type="text"
|
||||||
|
name="Email"
|
||||||
|
[(ngModel)]="email"
|
||||||
|
required
|
||||||
|
appInputVerbatim="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
<input
|
||||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
id="masterPassword"
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPassword"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPassword"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -37,21 +73,23 @@
|
|||||||
<div class="buttons with-rows">
|
<div class="buttons with-rows">
|
||||||
<div class="buttons-row">
|
<div class="buttons-row">
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||||
<b [hidden]="form.loading"><i class="fa fa-sign-in" aria-hidden="true"></i> {{'logIn' | i18n}}</b>
|
<b [hidden]="form.loading"
|
||||||
|
><i class="fa fa-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
|
||||||
|
>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/register" class="btn block">
|
<a routerLink="/register" class="btn block">
|
||||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i> {{'createAccount' | i18n}}
|
<i class="fa fa-pencil-square-o" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons-row">
|
<div class="buttons-row">
|
||||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||||
<i class="fa fa-bank" aria-hidden="true"></i> {{'enterpriseSingleSignOn' | i18n}}
|
<i class="fa fa-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-options">
|
<div class="sub-options">
|
||||||
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
|
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,53 +1,67 @@
|
|||||||
import {
|
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
Component,
|
|
||||||
NgZone,
|
|
||||||
OnDestroy,
|
|
||||||
ViewChild,
|
|
||||||
ViewContainerRef,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { EnvironmentComponent } from './environment.component';
|
import { EnvironmentComponent } from "./environment.component";
|
||||||
|
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
|
|
||||||
import { LoginComponent as BaseLoginComponent } from 'jslib-angular/components/login.component';
|
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'LoginComponent';
|
const BroadcasterSubscriptionId = "LoginComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: "app-login",
|
||||||
templateUrl: 'login.component.html',
|
templateUrl: "login.component.html",
|
||||||
})
|
})
|
||||||
export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
||||||
@ViewChild('environment', { read: ViewContainerRef, static: true }) environmentModal: ViewContainerRef;
|
@ViewChild("environment", { read: ViewContainerRef, static: true })
|
||||||
|
environmentModal: ViewContainerRef;
|
||||||
|
|
||||||
showingModal = false;
|
showingModal = false;
|
||||||
|
|
||||||
private deferFocus: boolean = null;
|
private deferFocus: boolean = null;
|
||||||
|
|
||||||
constructor(authService: AuthService, router: Router, i18nService: I18nService,
|
constructor(
|
||||||
syncService: SyncService, private modalService: ModalService,
|
authService: AuthService,
|
||||||
platformUtilsService: PlatformUtilsService, stateService: StateService,
|
router: Router,
|
||||||
environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService,
|
i18nService: I18nService,
|
||||||
cryptoFunctionService: CryptoFunctionService, private broadcasterService: BroadcasterService,
|
syncService: SyncService,
|
||||||
ngZone: NgZone, private messagingService: MessagingService,
|
private modalService: ModalService,
|
||||||
logService: LogService) {
|
platformUtilsService: PlatformUtilsService,
|
||||||
super(authService, router, platformUtilsService, i18nService, stateService, environmentService,
|
stateService: StateService,
|
||||||
passwordGenerationService, cryptoFunctionService, logService, ngZone);
|
environmentService: EnvironmentService,
|
||||||
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
ngZone: NgZone,
|
||||||
|
private messagingService: MessagingService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
authService,
|
||||||
|
router,
|
||||||
|
platformUtilsService,
|
||||||
|
i18nService,
|
||||||
|
stateService,
|
||||||
|
environmentService,
|
||||||
|
passwordGenerationService,
|
||||||
|
cryptoFunctionService,
|
||||||
|
logService,
|
||||||
|
ngZone
|
||||||
|
);
|
||||||
super.onSuccessfulLogin = () => {
|
super.onSuccessfulLogin = () => {
|
||||||
return syncService.fullSync(true);
|
return syncService.fullSync(true);
|
||||||
};
|
};
|
||||||
@ -58,10 +72,10 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'windowHidden':
|
case "windowHidden":
|
||||||
this.onWindowHidden();
|
this.onWindowHidden();
|
||||||
break;
|
break;
|
||||||
case 'windowIsFocused':
|
case "windowIsFocused":
|
||||||
if (this.deferFocus === null) {
|
if (this.deferFocus === null) {
|
||||||
this.deferFocus = !message.windowIsFocused;
|
this.deferFocus = !message.windowIsFocused;
|
||||||
if (!this.deferFocus) {
|
if (!this.deferFocus) {
|
||||||
@ -76,7 +90,7 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.messagingService.send('getWindowIsFocused');
|
this.messagingService.send("getWindowIsFocused");
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -84,7 +98,10 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async settings() {
|
async settings() {
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(EnvironmentComponent, this.environmentModal);
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
|
EnvironmentComponent,
|
||||||
|
this.environmentModal
|
||||||
|
);
|
||||||
|
|
||||||
modal.onShown.subscribe(() => {
|
modal.onShown.subscribe(() => {
|
||||||
this.showingModal = true;
|
this.showingModal = true;
|
||||||
@ -105,8 +122,8 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
async submit() {
|
async submit() {
|
||||||
await super.submit();
|
await super.submit();
|
||||||
if (this.captchaSiteKey) {
|
if (this.captchaSiteKey) {
|
||||||
const content = document.getElementById('content') as HTMLDivElement;
|
const content = document.getElementById("content") as HTMLDivElement;
|
||||||
content.setAttribute('style', 'width:335px');
|
content.setAttribute("style", "width:335px");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,64 +4,85 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="premiumTitle">
|
<div class="box-header" id="premiumTitle">
|
||||||
{{'premiumMembership' | i18n}}
|
{{ "premiumMembership" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content box-content-padded">
|
<div class="box-content box-content-padded">
|
||||||
<div *ngIf="!isPremium">
|
<div *ngIf="!isPremium">
|
||||||
<p class="text-center lead">{{'premiumNotCurrentMember' | i18n}}</p>
|
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
|
||||||
<p>{{'premiumSignUpAndGet' | i18n}}</p>
|
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
|
||||||
<ul class="fa-ul">
|
<ul class="fa-ul">
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||||
{{'premiumSignUpStorage' | i18n}}
|
{{ "premiumSignUpStorage" | i18n }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||||
{{'premiumSignUpTwoStep' | i18n}}
|
{{ "premiumSignUpTwoStep" | i18n }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||||
{{'premiumSignUpReports' | i18n}}
|
{{ "premiumSignUpReports" | i18n }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||||
{{'premiumSignUpTotp' | i18n}}
|
{{ "premiumSignUpTotp" | i18n }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||||
{{'premiumSignUpSupport' | i18n}}
|
{{ "premiumSignUpSupport" | i18n }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||||
{{'premiumSignUpFuture' | i18n}}
|
{{ "premiumSignUpFuture" | i18n }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="text-center lead no-margin">
|
<p class="text-center lead no-margin">
|
||||||
{{'premiumPrice' | i18n : (price | currency:'$')}}
|
{{ "premiumPrice" | i18n: (price | currency: "$") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isPremium">
|
<div *ngIf="isPremium">
|
||||||
<p class="text-center lead">{{'premiumCurrentMember' | i18n}}</p>
|
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
|
||||||
<p class="text-center">{{'premiumCurrentMemberThanks' | i18n}}</p>
|
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
||||||
<b>{{'premiumManage' | i18n}}</b>
|
<b>{{ "premiumManage" | i18n }}</b>
|
||||||
</button>
|
</button>
|
||||||
<button #purchaseBtn type="button" class="primary" appBlurClick (click)="purchase()" *ngIf="!isPremium"
|
<button
|
||||||
[disabled]="purchaseBtn.loading">
|
#purchaseBtn
|
||||||
<b>{{'premiumPurchase' | i18n}}</b>
|
type="button"
|
||||||
|
class="primary"
|
||||||
|
appBlurClick
|
||||||
|
(click)="purchase()"
|
||||||
|
*ngIf="!isPremium"
|
||||||
|
[disabled]="purchaseBtn.loading"
|
||||||
|
>
|
||||||
|
<b>{{ "premiumPurchase" | i18n }}</b>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
<div class="right" *ngIf="!isPremium">
|
<div class="right" *ngIf="!isPremium">
|
||||||
<button #refreshBtn type="button" appBlurClick (click)="refresh()" [disabled]="refreshBtn.loading"
|
<button
|
||||||
appA11yTitle="{{'premiumRefresh' | i18n}}" [appApiAction]="refreshPromise">
|
#refreshBtn
|
||||||
<i class="fa fa-refresh fa-lg fa-fw" [hidden]="refreshBtn.loading" aria-hidden="true"></i>
|
type="button"
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!refreshBtn.loading"
|
appBlurClick
|
||||||
aria-hidden="true"></i>
|
(click)="refresh()"
|
||||||
|
[disabled]="refreshBtn.loading"
|
||||||
|
appA11yTitle="{{ 'premiumRefresh' | i18n }}"
|
||||||
|
[appApiAction]="refreshPromise"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-refresh fa-lg fa-fw"
|
||||||
|
[hidden]="refreshBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!refreshBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,25 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
import { PremiumComponent as BasePremiumComponent } from 'jslib-angular/components/premium.component';
|
import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-premium',
|
selector: "app-premium",
|
||||||
templateUrl: 'premium.component.html',
|
templateUrl: "premium.component.html",
|
||||||
})
|
})
|
||||||
export class PremiumComponent extends BasePremiumComponent {
|
export class PremiumComponent extends BasePremiumComponent {
|
||||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
apiService: ApiService, logService: LogService,
|
i18nService: I18nService,
|
||||||
stateService: StateService) {
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
apiService: ApiService,
|
||||||
|
logService: LogService,
|
||||||
|
stateService: StateService
|
||||||
|
) {
|
||||||
super(i18nService, platformUtilsService, apiService, logService, stateService);
|
super(i18nService, platformUtilsService, apiService, logService, stateService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,93 +1,150 @@
|
|||||||
<form id="register-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
<form id="register-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>{{'createAccount' | i18n}}</h1>
|
<h1>{{ "createAccount" | i18n }}</h1>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
<input
|
||||||
[appAutofocus]="email === ''" appInputVerbatim>
|
id="email"
|
||||||
|
type="text"
|
||||||
|
name="Email"
|
||||||
|
[(ngModel)]="email"
|
||||||
|
required
|
||||||
|
[appAutofocus]="email === ''"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<div class="box-content-row-flex">
|
<div class="box-content-row-flex">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">
|
<label for="masterPassword">
|
||||||
{{'masterPass' | i18n}}
|
{{ "masterPass" | i18n }}
|
||||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
<strong
|
||||||
*ngIf="masterPasswordScoreText">
|
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||||
|
*ngIf="masterPasswordScoreText"
|
||||||
|
>
|
||||||
{{ masterPasswordScoreText }}
|
{{ masterPasswordScoreText }}
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
<input
|
||||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
id="masterPassword"
|
||||||
[appAutofocus]="email !== ''" (input)="updatePasswordStrength()" appInputVerbatim>
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPassword"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPassword"
|
||||||
|
required
|
||||||
|
[appAutofocus]="email !== ''"
|
||||||
|
(input)="updatePasswordStrength()"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword(false)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar" aria-valuenow="0"
|
<div
|
||||||
aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}"></div>
|
role="progressbar"
|
||||||
|
aria-valuenow="0"
|
||||||
|
aria-valuemin="0"
|
||||||
|
aria-valuemax="100"
|
||||||
|
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||||
|
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'masterPassDesc' | i18n}}
|
{{ "masterPassDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
<input
|
||||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="confirmMasterPassword" required
|
id="masterPasswordRetype"
|
||||||
appInputVerbatim>
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPasswordRetype"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="confirmMasterPassword"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword(true)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'masterPassHintDesc' | i18n}}
|
{{ "masterPassHintDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box last" *ngIf="showTerms">
|
<div class="box last" *ngIf="showTerms">
|
||||||
<div class="box-footer checkbox">
|
<div class="box-footer checkbox">
|
||||||
<input type="checkbox" id="acceptPolicies" [(ngModel)]="acceptPolicies" name="AcceptPolicies">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="acceptPolicies"
|
||||||
|
[(ngModel)]="acceptPolicies"
|
||||||
|
name="AcceptPolicies"
|
||||||
|
/>
|
||||||
<label for="acceptPolicies">
|
<label for="acceptPolicies">
|
||||||
{{'acceptPolicies' | i18n}}<br>
|
{{ "acceptPolicies" | i18n }}<br />
|
||||||
<a href="https://bitwarden.com/terms/" target="_blank"
|
<a href="https://bitwarden.com/terms/" target="_blank" rel="noopener">{{
|
||||||
rel="noopener">{{'termsOfService' | i18n}}</a>,
|
"termsOfService" | i18n
|
||||||
<a href="https://bitwarden.com/privacy/" target="_blank"
|
}}</a
|
||||||
rel="noopener">{{'privacyPolicy' | i18n}}</a>
|
>,
|
||||||
|
<a href="https://bitwarden.com/privacy/" target="_blank" rel="noopener">{{
|
||||||
|
"privacyPolicy" | i18n
|
||||||
|
}}</a>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,46 +1,59 @@
|
|||||||
import {
|
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||||
Component,
|
import { Router } from "@angular/router";
|
||||||
NgZone,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
} from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
import { RegisterComponent as BaseRegisterComponent } from 'jslib-angular/components/register.component';
|
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'RegisterComponent';
|
const BroadcasterSubscriptionId = "RegisterComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-register',
|
selector: "app-register",
|
||||||
templateUrl: 'register.component.html',
|
templateUrl: "register.component.html",
|
||||||
})
|
})
|
||||||
export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy {
|
export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy {
|
||||||
constructor(authService: AuthService, router: Router,
|
constructor(
|
||||||
i18nService: I18nService, cryptoService: CryptoService,
|
authService: AuthService,
|
||||||
apiService: ApiService, stateService: StateService,
|
router: Router,
|
||||||
platformUtilsService: PlatformUtilsService, passwordGenerationService: PasswordGenerationService,
|
i18nService: I18nService,
|
||||||
environmentService: EnvironmentService, private broadcasterService: BroadcasterService,
|
cryptoService: CryptoService,
|
||||||
private ngZone: NgZone, logService: LogService) {
|
apiService: ApiService,
|
||||||
super(authService, router, i18nService, cryptoService, apiService, stateService, platformUtilsService,
|
stateService: StateService,
|
||||||
passwordGenerationService, environmentService, logService);
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
environmentService: EnvironmentService,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
private ngZone: NgZone,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
authService,
|
||||||
|
router,
|
||||||
|
i18nService,
|
||||||
|
cryptoService,
|
||||||
|
apiService,
|
||||||
|
stateService,
|
||||||
|
platformUtilsService,
|
||||||
|
passwordGenerationService,
|
||||||
|
environmentService,
|
||||||
|
logService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'windowHidden':
|
case "windowHidden":
|
||||||
this.onWindowHidden();
|
this.onWindowHidden();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1,16 +1,26 @@
|
|||||||
<div id="remove-password-page" *ngIf="!loading">
|
<div id="remove-password-page" *ngIf="!loading">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>{{'removeMasterPassword' | i18n}}</h1>
|
<h1>{{ "removeMasterPassword" | i18n }}</h1>
|
||||||
<p>{{'convertOrganizationEncryptionDesc' | i18n : organization.name}}</p>
|
<p>{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}</p>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="btn primary block" [disabled]="actionPromise" appBlurClick
|
<button
|
||||||
(click)="convert()">
|
type="submit"
|
||||||
<b [hidden]="continuing">{{'removeMasterPassword' | i18n}}</b>
|
class="btn primary block"
|
||||||
|
[disabled]="actionPromise"
|
||||||
|
appBlurClick
|
||||||
|
(click)="convert()"
|
||||||
|
>
|
||||||
|
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn secondary block" [disabled]="actionPromise" appBlurClick
|
<button
|
||||||
(click)="leave()">
|
type="button"
|
||||||
<b [hidden]="leaving">{{'leaveOrganization' | i18n}}</b>
|
class="btn secondary block"
|
||||||
|
[disabled]="actionPromise"
|
||||||
|
appBlurClick
|
||||||
|
(click)="leave()"
|
||||||
|
>
|
||||||
|
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -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({
|
@Component({
|
||||||
selector: 'app-remove-password',
|
selector: "app-remove-password",
|
||||||
templateUrl: 'remove-password.component.html',
|
templateUrl: "remove-password.component.html",
|
||||||
})
|
})
|
||||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {
|
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
||||||
}
|
|
||||||
|
@ -1,56 +1,93 @@
|
|||||||
<form id="set-password-page" #form>
|
<form id="set-password-page" #form>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<img class="logo-image" alt="Bitwarden">
|
<img class="logo-image" alt="Bitwarden" />
|
||||||
<p class="lead">{{'setMasterPassword' | i18n}}</p>
|
<p class="lead">{{ "setMasterPassword" | i18n }}</p>
|
||||||
<div class="box text-center" *ngIf="syncLoading">
|
<div class="box text-center" *ngIf="syncLoading">
|
||||||
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||||
{{'loading' | i18n}}
|
{{ "loading" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!syncLoading">
|
<div *ngIf="!syncLoading">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<app-callout type="tip">{{'ssoCompleteRegistration' | i18n}}</app-callout>
|
<app-callout type="tip">{{ "ssoCompleteRegistration" | i18n }}</app-callout>
|
||||||
<app-callout type="warning" title="{{'resetPasswordPolicyAutoEnroll' | i18n}}"
|
<app-callout
|
||||||
*ngIf="resetPasswordAutoEnroll">
|
type="warning"
|
||||||
{{'resetPasswordAutoEnrollInviteWarning' | i18n}}
|
title="{{ 'resetPasswordPolicyAutoEnroll' | i18n }}"
|
||||||
|
*ngIf="resetPasswordAutoEnroll"
|
||||||
|
>
|
||||||
|
{{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
<app-callout
|
||||||
|
type="info"
|
||||||
|
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||||
|
*ngIf="enforcedPolicyOptions"
|
||||||
|
>
|
||||||
</app-callout>
|
</app-callout>
|
||||||
</div>
|
</div>
|
||||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
|
<form
|
||||||
|
#form
|
||||||
|
(ngSubmit)="submit()"
|
||||||
|
[appApiAction]="formPromise"
|
||||||
|
ngNativeValidate
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<div class="box-content-row-flex">
|
<div class="box-content-row-flex">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">{{'masterPass' | i18n}}
|
<label for="masterPassword"
|
||||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
>{{ "masterPass" | i18n }}
|
||||||
*ngIf="masterPasswordScoreText">
|
<strong
|
||||||
|
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||||
|
*ngIf="masterPasswordScoreText"
|
||||||
|
>
|
||||||
{{ masterPasswordScoreText }}
|
{{ masterPasswordScoreText }}
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
<input
|
||||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
id="masterPassword"
|
||||||
(input)="updatePasswordStrength()" appInputVerbatim>
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPassword"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPassword"
|
||||||
|
required
|
||||||
|
(input)="updatePasswordStrength()"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword(false)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar"
|
<div
|
||||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||||
[ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
role="progressbar"
|
||||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}">
|
aria-valuenow="0"
|
||||||
</div>
|
aria-valuemin="0"
|
||||||
|
aria-valuemax="100"
|
||||||
|
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||||
|
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'masterPassDesc' | i18n}}
|
{{ "masterPassDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
@ -58,16 +95,33 @@
|
|||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<div class="box-content-row-flex">
|
<div class="box-content-row-flex">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||||
<input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
|
<input
|
||||||
class="monospaced" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
|
id="masterPasswordRetype"
|
||||||
autocomplete="new-password">
|
type="password"
|
||||||
|
name="MasterPasswordRetype"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPasswordRetype"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword(true)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -77,22 +131,26 @@
|
|||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'masterPassHintDesc' | i18n}}
|
{{ "masterPassHintDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||||
<i *ngIf="form.loading" class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"
|
<i
|
||||||
aria-hidden="true"></i>
|
*ngIf="form.loading"
|
||||||
<span>{{'submit' | i18n}}</span>
|
class="fa fa-spinner fa-spin"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span>{{ "submit" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn block" (click)="logOut()">
|
<button class="btn block" (click)="logOut()">
|
||||||
<span>{{'logOut' | i18n}}</span>
|
<span>{{ "logOut" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,46 +1,55 @@
|
|||||||
import {
|
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||||
Component,
|
|
||||||
NgZone,
|
|
||||||
OnDestroy,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import {
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
ActivatedRoute,
|
|
||||||
Router,
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'SetPasswordComponent';
|
const BroadcasterSubscriptionId = "SetPasswordComponent";
|
||||||
|
|
||||||
import {
|
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
||||||
SetPasswordComponent as BaseSetPasswordComponent,
|
|
||||||
} from 'jslib-angular/components/set-password.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-set-password',
|
selector: "app-set-password",
|
||||||
templateUrl: 'set-password.component.html',
|
templateUrl: "set-password.component.html",
|
||||||
})
|
})
|
||||||
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
|
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
|
||||||
constructor(apiService: ApiService, i18nService: I18nService,
|
constructor(
|
||||||
cryptoService: CryptoService, messagingService: MessagingService,
|
apiService: ApiService,
|
||||||
passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
i18nService: I18nService,
|
||||||
policyService: PolicyService, router: Router,
|
cryptoService: CryptoService,
|
||||||
syncService: SyncService, route: ActivatedRoute,
|
messagingService: MessagingService,
|
||||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
stateService: StateService) {
|
platformUtilsService: PlatformUtilsService,
|
||||||
super(i18nService, cryptoService, messagingService, passwordGenerationService,
|
policyService: PolicyService,
|
||||||
platformUtilsService, policyService, router, apiService, syncService, route,
|
router: Router,
|
||||||
stateService);
|
syncService: SyncService,
|
||||||
|
route: ActivatedRoute,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
private ngZone: NgZone,
|
||||||
|
stateService: StateService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
i18nService,
|
||||||
|
cryptoService,
|
||||||
|
messagingService,
|
||||||
|
passwordGenerationService,
|
||||||
|
platformUtilsService,
|
||||||
|
policyService,
|
||||||
|
router,
|
||||||
|
apiService,
|
||||||
|
syncService,
|
||||||
|
route,
|
||||||
|
stateService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get masterPasswordScoreWidth() {
|
get masterPasswordScoreWidth() {
|
||||||
@ -50,26 +59,26 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
|
|||||||
get masterPasswordScoreColor() {
|
get masterPasswordScoreColor() {
|
||||||
switch (this.masterPasswordScore) {
|
switch (this.masterPasswordScore) {
|
||||||
case 4:
|
case 4:
|
||||||
return 'success';
|
return "success";
|
||||||
case 3:
|
case 3:
|
||||||
return 'primary';
|
return "primary";
|
||||||
case 2:
|
case 2:
|
||||||
return 'warning';
|
return "warning";
|
||||||
default:
|
default:
|
||||||
return 'danger';
|
return "danger";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get masterPasswordScoreText() {
|
get masterPasswordScoreText() {
|
||||||
switch (this.masterPasswordScore) {
|
switch (this.masterPasswordScore) {
|
||||||
case 4:
|
case 4:
|
||||||
return this.i18nService.t('strong');
|
return this.i18nService.t("strong");
|
||||||
case 3:
|
case 3:
|
||||||
return this.i18nService.t('good');
|
return this.i18nService.t("good");
|
||||||
case 2:
|
case 2:
|
||||||
return this.i18nService.t('weak');
|
return this.i18nService.t("weak");
|
||||||
default:
|
default:
|
||||||
return this.masterPasswordScore != null ? this.i18nService.t('weak') : null;
|
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +87,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
|
|||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'windowHidden':
|
case "windowHidden":
|
||||||
this.onWindowHidden();
|
this.onWindowHidden();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -4,43 +4,69 @@
|
|||||||
<div class="modal-body form">
|
<div class="modal-body form">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'security' | i18n}}
|
{{ "security" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content box-content-padded">
|
<div class="box-content box-content-padded">
|
||||||
<app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl></app-vault-timeout-input>
|
<app-vault-timeout-input
|
||||||
|
[vaultTimeouts]="vaultTimeouts"
|
||||||
|
[formControl]="vaultTimeout"
|
||||||
|
ngDefaultControl
|
||||||
|
></app-vault-timeout-input>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{'vaultTimeoutAction' | i18n}}</label>
|
<label>{{ "vaultTimeoutAction" | i18n }}</label>
|
||||||
<div class="radio radio-mt-2">
|
<div class="radio radio-mt-2">
|
||||||
<label for="vaultTimeoutActionLock">
|
<label for="vaultTimeoutActionLock">
|
||||||
<input type="radio" name="VaultTimeoutAction" id="vaultTimeoutActionLock"
|
<input
|
||||||
value="lock" [(ngModel)]="vaultTimeoutAction"
|
type="radio"
|
||||||
(change)="saveVaultTimeoutOptions()">
|
name="VaultTimeoutAction"
|
||||||
{{'lock' | i18n}}
|
id="vaultTimeoutActionLock"
|
||||||
|
value="lock"
|
||||||
|
[(ngModel)]="vaultTimeoutAction"
|
||||||
|
(change)="saveVaultTimeoutOptions()"
|
||||||
|
/>
|
||||||
|
{{ "lock" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'vaultTimeoutActionLockDesc' | i18n}}</small>
|
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label for="vaultTimeoutActionLogOut">
|
<label for="vaultTimeoutActionLogOut">
|
||||||
<input type="radio" name="VaultTimeoutAction" id="vaultTimeoutActionLogOut"
|
<input
|
||||||
value="logOut" [(ngModel)]="vaultTimeoutAction"
|
type="radio"
|
||||||
(change)="saveVaultTimeoutOptions()">
|
name="VaultTimeoutAction"
|
||||||
{{'logOut' | i18n}}
|
id="vaultTimeoutActionLogOut"
|
||||||
|
value="logOut"
|
||||||
|
[(ngModel)]="vaultTimeoutAction"
|
||||||
|
(change)="saveVaultTimeoutOptions()"
|
||||||
|
/>
|
||||||
|
{{ "logOut" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'vaultTimeoutActionLogOutDesc' | i18n}}</small>
|
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="pin">
|
<label for="pin">
|
||||||
<input id="pin" type="checkbox" name="PIN" [(ngModel)]="pin" (change)="updatePin()">
|
<input
|
||||||
{{'unlockWithPin' | i18n}}
|
id="pin"
|
||||||
|
type="checkbox"
|
||||||
|
name="PIN"
|
||||||
|
[(ngModel)]="pin"
|
||||||
|
(change)="updatePin()"
|
||||||
|
/>
|
||||||
|
{{ "unlockWithPin" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" *ngIf="supportsBiometric">
|
<div class="form-group" *ngIf="supportsBiometric">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="biometric">
|
<label for="biometric">
|
||||||
<input id="biometric" type="checkbox" name="biometric" [checked]="biometric" (change)="updateBiometric()">
|
<input
|
||||||
|
id="biometric"
|
||||||
|
type="checkbox"
|
||||||
|
name="biometric"
|
||||||
|
[checked]="biometric"
|
||||||
|
(change)="updateBiometric()"
|
||||||
|
/>
|
||||||
{{ biometricText | i18n }}
|
{{ biometricText | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -48,8 +74,14 @@
|
|||||||
<div class="form-group" *ngIf="supportsBiometric">
|
<div class="form-group" *ngIf="supportsBiometric">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="noAutoPromptBiometrics">
|
<label for="noAutoPromptBiometrics">
|
||||||
<input id="noAutoPromptBiometrics" type="checkbox" name="noAutoPromptBiometrics" [(ngModel)]="noAutoPromptBiometrics"
|
<input
|
||||||
[disabled]="!biometric" (change)="updateNoAutoPromptBiometrics()">
|
id="noAutoPromptBiometrics"
|
||||||
|
type="checkbox"
|
||||||
|
name="noAutoPromptBiometrics"
|
||||||
|
[(ngModel)]="noAutoPromptBiometrics"
|
||||||
|
[disabled]="!biometric"
|
||||||
|
(change)="updateNoAutoPromptBiometrics()"
|
||||||
|
/>
|
||||||
{{ noAutoPromptBiometricsText | i18n }}
|
{{ noAutoPromptBiometricsText | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -58,63 +90,96 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'options' | i18n}}
|
{{ "options" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content box-content-padded">
|
<div class="box-content box-content-padded">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="clearClipboard">{{'clearClipboard' | i18n}}</label>
|
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
||||||
<select id="clearClipboard" name="ClearClipboard" [(ngModel)]="clearClipboard"
|
<select
|
||||||
(change)="saveClearClipboard()">
|
id="clearClipboard"
|
||||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">{{o.name}}</option>
|
name="ClearClipboard"
|
||||||
|
[(ngModel)]="clearClipboard"
|
||||||
|
(change)="saveClearClipboard()"
|
||||||
|
>
|
||||||
|
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
||||||
|
{{ o.name }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="help-block">{{'clearClipboardDesc' | i18n}}</small>
|
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="minimizeOnCopyToClipboard">
|
<label for="minimizeOnCopyToClipboard">
|
||||||
<input id="minimizeOnCopyToClipboard" type="checkbox"
|
<input
|
||||||
name="MinimizeOnCopyToClipboard" [(ngModel)]="minimizeOnCopyToClipboard"
|
id="minimizeOnCopyToClipboard"
|
||||||
(change)="saveMinOnCopyToClipboard()">
|
type="checkbox"
|
||||||
{{'minimizeOnCopyToClipboard' | i18n}}
|
name="MinimizeOnCopyToClipboard"
|
||||||
|
[(ngModel)]="minimizeOnCopyToClipboard"
|
||||||
|
(change)="saveMinOnCopyToClipboard()"
|
||||||
|
/>
|
||||||
|
{{ "minimizeOnCopyToClipboard" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'minimizeOnCopyToClipboardDesc' | i18n}}</small>
|
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="disableFavicons">
|
<label for="disableFavicons">
|
||||||
<input id="disableFavicons" type="checkbox" name="DisableFavicons"
|
<input
|
||||||
[(ngModel)]="disableFavicons" (change)="saveFavicons()">
|
id="disableFavicons"
|
||||||
{{'disableFavicon' | i18n}}
|
type="checkbox"
|
||||||
|
name="DisableFavicons"
|
||||||
|
[(ngModel)]="disableFavicons"
|
||||||
|
(change)="saveFavicons()"
|
||||||
|
/>
|
||||||
|
{{ "disableFavicon" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'disableFaviconDesc' | i18n}}</small>
|
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="enableBrowserIntegration">
|
<label for="enableBrowserIntegration">
|
||||||
<input id="enableBrowserIntegration" type="checkbox" name="EnableBrowserIntegration"
|
<input
|
||||||
[(ngModel)]="enableBrowserIntegration" (change)="saveBrowserIntegration()">
|
id="enableBrowserIntegration"
|
||||||
{{'enableBrowserIntegration' | i18n}}
|
type="checkbox"
|
||||||
|
name="EnableBrowserIntegration"
|
||||||
|
[(ngModel)]="enableBrowserIntegration"
|
||||||
|
(change)="saveBrowserIntegration()"
|
||||||
|
/>
|
||||||
|
{{ "enableBrowserIntegration" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'enableBrowserIntegrationDesc' | i18n}}</small>
|
<small class="help-block">{{ "enableBrowserIntegrationDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="enableBrowserIntegrationFingerprint">
|
<label for="enableBrowserIntegrationFingerprint">
|
||||||
<input id="enableBrowserIntegrationFingerprint" type="checkbox" name="EnableBrowserIntegrationFingerprint"
|
<input
|
||||||
[(ngModel)]="enableBrowserIntegrationFingerprint" (change)="saveBrowserIntegrationFingerprint()" [disabled]="!enableBrowserIntegration">
|
id="enableBrowserIntegrationFingerprint"
|
||||||
{{'enableBrowserIntegrationFingerprint' | i18n}}
|
type="checkbox"
|
||||||
|
name="EnableBrowserIntegrationFingerprint"
|
||||||
|
[(ngModel)]="enableBrowserIntegrationFingerprint"
|
||||||
|
(change)="saveBrowserIntegrationFingerprint()"
|
||||||
|
[disabled]="!enableBrowserIntegration"
|
||||||
|
/>
|
||||||
|
{{ "enableBrowserIntegrationFingerprint" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'enableBrowserIntegrationFingerprintDesc' | i18n}}</small>
|
<small class="help-block">{{
|
||||||
|
"enableBrowserIntegrationFingerprintDesc" | i18n
|
||||||
|
}}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="enableTray">
|
<label for="enableTray">
|
||||||
<input id="enableTray" type="checkbox" name="EnableTray" [(ngModel)]="enableTray"
|
<input
|
||||||
(change)="saveTray()">
|
id="enableTray"
|
||||||
|
type="checkbox"
|
||||||
|
name="EnableTray"
|
||||||
|
[(ngModel)]="enableTray"
|
||||||
|
(change)="saveTray()"
|
||||||
|
/>
|
||||||
{{ enableTrayText }}
|
{{ enableTrayText }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -123,8 +188,13 @@
|
|||||||
<div class="form-group" *ngIf="showMinToTray">
|
<div class="form-group" *ngIf="showMinToTray">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="enableMinToTray">
|
<label for="enableMinToTray">
|
||||||
<input id="enableMinToTray" type="checkbox" name="EnableMinToTray"
|
<input
|
||||||
[(ngModel)]="enableMinToTray" (change)="saveMinToTray()">
|
id="enableMinToTray"
|
||||||
|
type="checkbox"
|
||||||
|
name="EnableMinToTray"
|
||||||
|
[(ngModel)]="enableMinToTray"
|
||||||
|
(change)="saveMinToTray()"
|
||||||
|
/>
|
||||||
{{ enableMinToTrayText }}
|
{{ enableMinToTrayText }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -133,8 +203,13 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="enableCloseToTray">
|
<label for="enableCloseToTray">
|
||||||
<input id="enableCloseToTray" type="checkbox" name="EnableCloseToTray"
|
<input
|
||||||
[(ngModel)]="enableCloseToTray" (change)="saveCloseToTray()">
|
id="enableCloseToTray"
|
||||||
|
type="checkbox"
|
||||||
|
name="EnableCloseToTray"
|
||||||
|
[(ngModel)]="enableCloseToTray"
|
||||||
|
(change)="saveCloseToTray()"
|
||||||
|
/>
|
||||||
{{ enableCloseToTrayText }}
|
{{ enableCloseToTrayText }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -143,8 +218,13 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="startToTray">
|
<label for="startToTray">
|
||||||
<input id="startToTray" type="checkbox" name="StartToTray" [(ngModel)]="startToTray"
|
<input
|
||||||
(change)="saveStartToTray()">
|
id="startToTray"
|
||||||
|
type="checkbox"
|
||||||
|
name="StartToTray"
|
||||||
|
[(ngModel)]="startToTray"
|
||||||
|
(change)="saveStartToTray()"
|
||||||
|
/>
|
||||||
{{ startToTrayText }}
|
{{ startToTrayText }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -153,42 +233,52 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="openAtLogin">
|
<label for="openAtLogin">
|
||||||
<input id="openAtLogin" type="checkbox" name="OpenAtLogin" [(ngModel)]="openAtLogin"
|
<input
|
||||||
(change)="saveOpenAtLogin()">
|
id="openAtLogin"
|
||||||
{{'openAtLogin' | i18n}}
|
type="checkbox"
|
||||||
|
name="OpenAtLogin"
|
||||||
|
[(ngModel)]="openAtLogin"
|
||||||
|
(change)="saveOpenAtLogin()"
|
||||||
|
/>
|
||||||
|
{{ "openAtLogin" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'openAtLoginDesc' | i18n}}</small>
|
<small class="help-block">{{ "openAtLoginDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" *ngIf="showAlwaysShowDock">
|
<div class="form-group" *ngIf="showAlwaysShowDock">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="alwaysShowDock">
|
<label for="alwaysShowDock">
|
||||||
<input id="alwaysShowDock" type="checkbox" name="AlwaysShowDock" [(ngModel)]="alwaysShowDock"
|
<input
|
||||||
(change)="saveAlwaysShowDock()">
|
id="alwaysShowDock"
|
||||||
{{'alwaysShowDock' | i18n}}
|
type="checkbox"
|
||||||
|
name="AlwaysShowDock"
|
||||||
|
[(ngModel)]="alwaysShowDock"
|
||||||
|
(change)="saveAlwaysShowDock()"
|
||||||
|
/>
|
||||||
|
{{ "alwaysShowDock" | i18n }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'alwaysShowDockDesc' | i18n}}</small>
|
<small class="help-block">{{ "alwaysShowDockDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="theme">{{'theme' | i18n}}</label>
|
<label for="theme">{{ "theme" | i18n }}</label>
|
||||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="help-block">{{'themeDesc' | i18n}}</small>
|
<small class="help-block">{{ "themeDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="locale">{{'language' | i18n}}</label>
|
<label for="locale">{{ "language" | i18n }}</label>
|
||||||
<select id="locale" name="Locale" [(ngModel)]="locale" (change)="saveLocale()">
|
<select id="locale" name="Locale" [(ngModel)]="locale" (change)="saveLocale()">
|
||||||
<option *ngFor="let o of localeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of localeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="help-block">{{'languageDesc' | i18n}}</small>
|
<small class="help-block">{{ "languageDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,32 +1,29 @@
|
|||||||
import {
|
import { Component, OnInit } from "@angular/core";
|
||||||
Component,
|
import { FormControl } from "@angular/forms";
|
||||||
OnInit,
|
import { debounceTime } from "rxjs/operators";
|
||||||
} from '@angular/core';
|
|
||||||
import { FormControl } from '@angular/forms';
|
|
||||||
import { debounceTime } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { DeviceType } from 'jslib-common/enums/deviceType';
|
import { DeviceType } from "jslib-common/enums/deviceType";
|
||||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
import { ThemeType } from "jslib-common/enums/themeType";
|
||||||
|
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.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";
|
||||||
|
|
||||||
import { Utils } from 'jslib-common/misc/utils';
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
import { isWindowsStore } from 'jslib-electron/utils';
|
import { isWindowsStore } from "jslib-electron/utils";
|
||||||
|
|
||||||
import { StorageLocation } from 'jslib-common/enums/storageLocation';
|
import { StorageLocation } from "jslib-common/enums/storageLocation";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings',
|
selector: "app-settings",
|
||||||
templateUrl: 'settings.component.html',
|
templateUrl: "settings.component.html",
|
||||||
})
|
})
|
||||||
export class SettingsComponent implements OnInit {
|
export class SettingsComponent implements OnInit {
|
||||||
vaultTimeoutAction: string;
|
vaultTimeoutAction: string;
|
||||||
@ -68,50 +65,55 @@ export class SettingsComponent implements OnInit {
|
|||||||
|
|
||||||
vaultTimeout: FormControl = new FormControl(null);
|
vaultTimeout: FormControl = new FormControl(null);
|
||||||
|
|
||||||
constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
private vaultTimeoutService: VaultTimeoutService, private stateService: StateService,
|
private i18nService: I18nService,
|
||||||
private messagingService: MessagingService, private cryptoService: CryptoService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private modalService: ModalService) {
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private messagingService: MessagingService,
|
||||||
|
private cryptoService: CryptoService,
|
||||||
|
private modalService: ModalService
|
||||||
|
) {
|
||||||
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||||
|
|
||||||
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
|
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
|
||||||
this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
||||||
|
|
||||||
const trayKey = isMac ? 'enableMenuBar' : 'enableTray';
|
const trayKey = isMac ? "enableMenuBar" : "enableTray";
|
||||||
this.enableTrayText = this.i18nService.t(trayKey);
|
this.enableTrayText = this.i18nService.t(trayKey);
|
||||||
this.enableTrayDescText = this.i18nService.t(trayKey + 'Desc');
|
this.enableTrayDescText = this.i18nService.t(trayKey + "Desc");
|
||||||
|
|
||||||
const minToTrayKey = isMac ? 'enableMinToMenuBar' : 'enableMinToTray';
|
const minToTrayKey = isMac ? "enableMinToMenuBar" : "enableMinToTray";
|
||||||
this.enableMinToTrayText = this.i18nService.t(minToTrayKey);
|
this.enableMinToTrayText = this.i18nService.t(minToTrayKey);
|
||||||
this.enableMinToTrayDescText = this.i18nService.t(minToTrayKey + 'Desc');
|
this.enableMinToTrayDescText = this.i18nService.t(minToTrayKey + "Desc");
|
||||||
|
|
||||||
const closeToTrayKey = isMac ? 'enableCloseToMenuBar' : 'enableCloseToTray';
|
const closeToTrayKey = isMac ? "enableCloseToMenuBar" : "enableCloseToTray";
|
||||||
this.enableCloseToTrayText = this.i18nService.t(closeToTrayKey);
|
this.enableCloseToTrayText = this.i18nService.t(closeToTrayKey);
|
||||||
this.enableCloseToTrayDescText = this.i18nService.t(closeToTrayKey + 'Desc');
|
this.enableCloseToTrayDescText = this.i18nService.t(closeToTrayKey + "Desc");
|
||||||
|
|
||||||
const startToTrayKey = isMac ? 'startToMenuBar' : 'startToTray';
|
const startToTrayKey = isMac ? "startToMenuBar" : "startToTray";
|
||||||
this.startToTrayText = this.i18nService.t(startToTrayKey);
|
this.startToTrayText = this.i18nService.t(startToTrayKey);
|
||||||
this.startToTrayDescText = this.i18nService.t(startToTrayKey + 'Desc');
|
this.startToTrayDescText = this.i18nService.t(startToTrayKey + "Desc");
|
||||||
|
|
||||||
this.vaultTimeouts = [
|
this.vaultTimeouts = [
|
||||||
// { name: i18nService.t('immediately'), value: 0 },
|
// { name: i18nService.t('immediately'), value: 0 },
|
||||||
{ name: i18nService.t('oneMinute'), value: 1 },
|
{ name: i18nService.t("oneMinute"), value: 1 },
|
||||||
{ name: i18nService.t('fiveMinutes'), value: 5 },
|
{ name: i18nService.t("fiveMinutes"), value: 5 },
|
||||||
{ name: i18nService.t('fifteenMinutes'), value: 15 },
|
{ name: i18nService.t("fifteenMinutes"), value: 15 },
|
||||||
{ name: i18nService.t('thirtyMinutes'), value: 30 },
|
{ name: i18nService.t("thirtyMinutes"), value: 30 },
|
||||||
{ name: i18nService.t('oneHour'), value: 60 },
|
{ name: i18nService.t("oneHour"), value: 60 },
|
||||||
{ name: i18nService.t('fourHours'), value: 240 },
|
{ name: i18nService.t("fourHours"), value: 240 },
|
||||||
{ name: i18nService.t('onIdle'), value: -4 },
|
{ name: i18nService.t("onIdle"), value: -4 },
|
||||||
{ name: i18nService.t('onSleep'), value: -3 },
|
{ name: i18nService.t("onSleep"), value: -3 },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
|
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
|
||||||
this.vaultTimeouts.push({ name: i18nService.t('onLocked'), value: -2 });
|
this.vaultTimeouts.push({ name: i18nService.t("onLocked"), value: -2 });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vaultTimeouts = this.vaultTimeouts.concat([
|
this.vaultTimeouts = this.vaultTimeouts.concat([
|
||||||
{ name: i18nService.t('onRestart'), value: -1 },
|
{ name: i18nService.t("onRestart"), value: -1 },
|
||||||
{ name: i18nService.t('never'), value: null },
|
{ name: i18nService.t("never"), value: null },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
|
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
|
||||||
@ -119,32 +121,32 @@ export class SettingsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const localeOptions: any[] = [];
|
const localeOptions: any[] = [];
|
||||||
i18nService.supportedTranslationLocales.forEach(locale => {
|
i18nService.supportedTranslationLocales.forEach((locale) => {
|
||||||
let name = locale;
|
let name = locale;
|
||||||
if (i18nService.localeNames.has(locale)) {
|
if (i18nService.localeNames.has(locale)) {
|
||||||
name += (' - ' + i18nService.localeNames.get(locale));
|
name += " - " + i18nService.localeNames.get(locale);
|
||||||
}
|
}
|
||||||
localeOptions.push({ name: name, value: locale });
|
localeOptions.push({ name: name, value: locale });
|
||||||
});
|
});
|
||||||
localeOptions.sort(Utils.getSortFunction(i18nService, 'name'));
|
localeOptions.sort(Utils.getSortFunction(i18nService, "name"));
|
||||||
localeOptions.splice(0, 0, { name: i18nService.t('default'), value: null });
|
localeOptions.splice(0, 0, { name: i18nService.t("default"), value: null });
|
||||||
this.localeOptions = localeOptions;
|
this.localeOptions = localeOptions;
|
||||||
|
|
||||||
this.themeOptions = [
|
this.themeOptions = [
|
||||||
{ name: i18nService.t('default'), value: null },
|
{ name: i18nService.t("default"), value: null },
|
||||||
{ name: i18nService.t('light'), value: ThemeType.Light },
|
{ name: i18nService.t("light"), value: ThemeType.Light },
|
||||||
{ name: i18nService.t('dark'), value: ThemeType.Dark },
|
{ name: i18nService.t("dark"), value: ThemeType.Dark },
|
||||||
{ name: 'Nord', value: ThemeType.Nord },
|
{ name: "Nord", value: ThemeType.Nord },
|
||||||
];
|
];
|
||||||
|
|
||||||
this.clearClipboardOptions = [
|
this.clearClipboardOptions = [
|
||||||
{ name: i18nService.t('never'), value: null },
|
{ name: i18nService.t("never"), value: null },
|
||||||
{ name: i18nService.t('tenSeconds'), value: 10 },
|
{ name: i18nService.t("tenSeconds"), value: 10 },
|
||||||
{ name: i18nService.t('twentySeconds'), value: 20 },
|
{ name: i18nService.t("twentySeconds"), value: 20 },
|
||||||
{ name: i18nService.t('thirtySeconds'), value: 30 },
|
{ name: i18nService.t("thirtySeconds"), value: 30 },
|
||||||
{ name: i18nService.t('oneMinute'), value: 60 },
|
{ name: i18nService.t("oneMinute"), value: 60 },
|
||||||
{ name: i18nService.t('twoMinutes'), value: 120 },
|
{ name: i18nService.t("twoMinutes"), value: 120 },
|
||||||
{ name: i18nService.t('fiveMinutes'), value: 300 },
|
{ name: i18nService.t("fiveMinutes"), value: 300 },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +158,8 @@ export class SettingsComponent implements OnInit {
|
|||||||
this.pin = pinSet[0] || pinSet[1];
|
this.pin = pinSet[0] || pinSet[1];
|
||||||
this.disableFavicons = await this.stateService.getDisableFavicon();
|
this.disableFavicons = await this.stateService.getDisableFavicon();
|
||||||
this.enableBrowserIntegration = await this.stateService.getEnableBrowserIntegration();
|
this.enableBrowserIntegration = await this.stateService.getEnableBrowserIntegration();
|
||||||
this.enableBrowserIntegrationFingerprint = await this.stateService.getEnableBrowserIntegrationFingerprint();
|
this.enableBrowserIntegrationFingerprint =
|
||||||
|
await this.stateService.getEnableBrowserIntegrationFingerprint();
|
||||||
this.enableMinToTray = await this.stateService.getEnableMinimizeToTray();
|
this.enableMinToTray = await this.stateService.getEnableMinimizeToTray();
|
||||||
this.enableCloseToTray = await this.stateService.getEnableCloseToTray();
|
this.enableCloseToTray = await this.stateService.getEnableCloseToTray();
|
||||||
this.enableTray = await this.stateService.getEnableTray();
|
this.enableTray = await this.stateService.getEnableTray();
|
||||||
@ -176,13 +179,16 @@ export class SettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveVaultTimeoutOptions() {
|
async saveVaultTimeoutOptions() {
|
||||||
if (this.vaultTimeoutAction === 'logOut') {
|
if (this.vaultTimeoutAction === "logOut") {
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('vaultTimeoutLogOutConfirmation'),
|
this.i18nService.t("vaultTimeoutLogOutConfirmation"),
|
||||||
this.i18nService.t('vaultTimeoutLogOutConfirmationTitle'),
|
this.i18nService.t("vaultTimeoutLogOutConfirmationTitle"),
|
||||||
this.i18nService.t('yes'), this.i18nService.t('cancel'), 'warning');
|
this.i18nService.t("yes"),
|
||||||
|
this.i18nService.t("cancel"),
|
||||||
|
"warning"
|
||||||
|
);
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
this.vaultTimeoutAction = 'lock';
|
this.vaultTimeoutAction = "lock";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,11 +199,18 @@ export class SettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.vaultTimeout.valid) {
|
if (!this.vaultTimeout.valid) {
|
||||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutTooLarge'));
|
this.platformUtilsService.showToast(
|
||||||
|
"error",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("vaultTimeoutTooLarge")
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
await this.vaultTimeoutService.setVaultTimeoutOptions(
|
||||||
|
this.vaultTimeout.value,
|
||||||
|
this.vaultTimeoutAction
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePin() {
|
async updatePin() {
|
||||||
@ -252,8 +265,10 @@ export class SettingsComponent implements OnInit {
|
|||||||
|
|
||||||
async saveFavicons() {
|
async saveFavicons() {
|
||||||
await this.stateService.setDisableFavicon(this.disableFavicons);
|
await this.stateService.setDisableFavicon(this.disableFavicons);
|
||||||
await this.stateService.setDisableFavicon(this.disableFavicons, { storageLocation: StorageLocation.Disk });
|
await this.stateService.setDisableFavicon(this.disableFavicons, {
|
||||||
this.messagingService.send('refreshCiphers');
|
storageLocation: StorageLocation.Disk,
|
||||||
|
});
|
||||||
|
this.messagingService.send("refreshCiphers");
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveMinToTray() {
|
async saveMinToTray() {
|
||||||
@ -270,10 +285,18 @@ export class SettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveTray() {
|
async saveTray() {
|
||||||
if (this.requireEnableTray && !this.enableTray && (this.startToTray || this.enableCloseToTray)) {
|
if (
|
||||||
|
this.requireEnableTray &&
|
||||||
|
!this.enableTray &&
|
||||||
|
(this.startToTray || this.enableCloseToTray)
|
||||||
|
) {
|
||||||
const confirm = await this.platformUtilsService.showDialog(
|
const confirm = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('confirmTrayDesc'), this.i18nService.t('confirmTrayTitle'),
|
this.i18nService.t("confirmTrayDesc"),
|
||||||
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
this.i18nService.t("confirmTrayTitle"),
|
||||||
|
this.i18nService.t("yes"),
|
||||||
|
this.i18nService.t("no"),
|
||||||
|
"warning"
|
||||||
|
);
|
||||||
|
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
this.startToTray = false;
|
this.startToTray = false;
|
||||||
@ -288,7 +311,7 @@ export class SettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.stateService.setEnableTray(this.enableTray);
|
await this.stateService.setEnableTray(this.enableTray);
|
||||||
this.messagingService.send(this.enableTray ? 'showTray' : 'removeTray');
|
this.messagingService.send(this.enableTray ? "showTray" : "removeTray");
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveStartToTray() {
|
async saveStartToTray() {
|
||||||
@ -323,30 +346,38 @@ export class SettingsComponent implements OnInit {
|
|||||||
|
|
||||||
async saveOpenAtLogin() {
|
async saveOpenAtLogin() {
|
||||||
this.stateService.setOpenAtLogin(this.openAtLogin);
|
this.stateService.setOpenAtLogin(this.openAtLogin);
|
||||||
this.messagingService.send(this.openAtLogin ? 'addOpenAtLogin' : 'removeOpenAtLogin');
|
this.messagingService.send(this.openAtLogin ? "addOpenAtLogin" : "removeOpenAtLogin");
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveBrowserIntegration() {
|
async saveBrowserIntegration() {
|
||||||
if (process.platform === 'darwin' && !this.platformUtilsService.isMacAppStore()) {
|
if (process.platform === "darwin" && !this.platformUtilsService.isMacAppStore()) {
|
||||||
await this.platformUtilsService.showDialog(
|
await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('browserIntegrationMasOnlyDesc'),
|
this.i18nService.t("browserIntegrationMasOnlyDesc"),
|
||||||
this.i18nService.t('browserIntegrationMasOnlyTitle'),
|
this.i18nService.t("browserIntegrationMasOnlyTitle"),
|
||||||
this.i18nService.t('ok'), null, 'warning');
|
this.i18nService.t("ok"),
|
||||||
|
null,
|
||||||
|
"warning"
|
||||||
|
);
|
||||||
|
|
||||||
this.enableBrowserIntegration = false;
|
this.enableBrowserIntegration = false;
|
||||||
return;
|
return;
|
||||||
} else if (isWindowsStore()) {
|
} else if (isWindowsStore()) {
|
||||||
await this.platformUtilsService.showDialog(
|
await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('browserIntegrationWindowsStoreDesc'),
|
this.i18nService.t("browserIntegrationWindowsStoreDesc"),
|
||||||
this.i18nService.t('browserIntegrationWindowsStoreTitle'),
|
this.i18nService.t("browserIntegrationWindowsStoreTitle"),
|
||||||
this.i18nService.t('ok'), null, 'warning');
|
this.i18nService.t("ok"),
|
||||||
|
null,
|
||||||
|
"warning"
|
||||||
|
);
|
||||||
|
|
||||||
this.enableBrowserIntegration = false;
|
this.enableBrowserIntegration = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.stateService.setEnableBrowserIntegration(this.enableBrowserIntegration);
|
await this.stateService.setEnableBrowserIntegration(this.enableBrowserIntegration);
|
||||||
this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration');
|
this.messagingService.send(
|
||||||
|
this.enableBrowserIntegration ? "enableBrowserIntegration" : "disableBrowserIntegration"
|
||||||
|
);
|
||||||
|
|
||||||
if (!this.enableBrowserIntegration) {
|
if (!this.enableBrowserIntegration) {
|
||||||
this.enableBrowserIntegrationFingerprint = false;
|
this.enableBrowserIntegrationFingerprint = false;
|
||||||
@ -355,6 +386,8 @@ export class SettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveBrowserIntegrationFingerprint() {
|
async saveBrowserIntegrationFingerprint() {
|
||||||
await this.stateService.setEnableBrowserIntegrationFingerprint(this.enableBrowserIntegrationFingerprint);
|
await this.stateService.setEnableBrowserIntegrationFingerprint(
|
||||||
|
this.enableBrowserIntegrationFingerprint
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<form id="sso-page" (ngSubmit)="submit()">
|
<form id="sso-page" (ngSubmit)="submit()">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<img class="logo-image" alt="Bitwarden">
|
<img class="logo-image" alt="Bitwarden" />
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||||
{{'loading' | i18n}}
|
{{ "loading" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,40 +1,56 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import {
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
ActivatedRoute,
|
|
||||||
Router,
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component';
|
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sso',
|
selector: "app-sso",
|
||||||
templateUrl: 'sso.component.html',
|
templateUrl: "sso.component.html",
|
||||||
})
|
})
|
||||||
export class SsoComponent extends BaseSsoComponent {
|
export class SsoComponent extends BaseSsoComponent {
|
||||||
constructor(authService: AuthService, router: Router,
|
constructor(
|
||||||
i18nService: I18nService, syncService: SyncService, route: ActivatedRoute,
|
authService: AuthService,
|
||||||
stateService: StateService, platformUtilsService: PlatformUtilsService,
|
router: Router,
|
||||||
apiService: ApiService, cryptoFunctionService: CryptoFunctionService,
|
i18nService: I18nService,
|
||||||
environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService,
|
syncService: SyncService,
|
||||||
logService: LogService) {
|
route: ActivatedRoute,
|
||||||
super(authService, router, i18nService, route, stateService, platformUtilsService,
|
stateService: StateService,
|
||||||
apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService);
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
apiService: ApiService,
|
||||||
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
|
environmentService: EnvironmentService,
|
||||||
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
authService,
|
||||||
|
router,
|
||||||
|
i18nService,
|
||||||
|
route,
|
||||||
|
stateService,
|
||||||
|
platformUtilsService,
|
||||||
|
apiService,
|
||||||
|
cryptoFunctionService,
|
||||||
|
environmentService,
|
||||||
|
passwordGenerationService,
|
||||||
|
logService
|
||||||
|
);
|
||||||
super.onSuccessfulLogin = () => {
|
super.onSuccessfulLogin = () => {
|
||||||
return syncService.fullSync(true);
|
return syncService.fullSync(true);
|
||||||
};
|
};
|
||||||
this.redirectUri = 'bitwarden://sso-callback';
|
this.redirectUri = "bitwarden://sso-callback";
|
||||||
this.clientId = 'desktop';
|
this.clientId = "desktop";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,29 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="twoStepTitle">
|
<div class="box-header" id="twoStepTitle">
|
||||||
{{'twoStepOptions' | i18n}}
|
{{ "twoStepOptions" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<a href="#" appStopClick *ngFor="let p of providers" class="box-content-row"
|
<a
|
||||||
(click)="choose(p)">
|
href="#"
|
||||||
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right">
|
appStopClick
|
||||||
|
*ngFor="let p of providers"
|
||||||
|
class="box-content-row"
|
||||||
|
(click)="choose(p)"
|
||||||
|
>
|
||||||
|
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right" />
|
||||||
<span class="text">{{ p.name }}</span>
|
<span class="text">{{ p.name }}</span>
|
||||||
<span class="detail">{{ p.description }}</span>
|
<span class="detail">{{ p.description }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
||||||
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
|
<span class="text">{{ "recoveryCodeTitle" | i18n }}</span>
|
||||||
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
|
<span class="detail">{{ "recoveryCodeDesc" | i18n }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { Router } from '@angular/router';
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import {
|
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||||
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
|
|
||||||
} from 'jslib-angular/components/two-factor-options.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-two-factor-options',
|
selector: "app-two-factor-options",
|
||||||
templateUrl: 'two-factor-options.component.html',
|
templateUrl: "two-factor-options.component.html",
|
||||||
})
|
})
|
||||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||||
constructor(authService: AuthService, router: Router,
|
constructor(
|
||||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
|
authService: AuthService,
|
||||||
|
router: Router,
|
||||||
|
i18nService: I18nService,
|
||||||
|
platformUtilsService: PlatformUtilsService
|
||||||
|
) {
|
||||||
super(authService, router, i18nService, platformUtilsService, window);
|
super(authService, router, i18nService, platformUtilsService, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,64 @@
|
|||||||
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise" attr.aria-hidden="{{showingModal}}">
|
<form
|
||||||
|
id="two-factor-page"
|
||||||
|
#form
|
||||||
|
(ngSubmit)="submit()"
|
||||||
|
[appApiAction]="formPromise"
|
||||||
|
attr.aria-hidden="{{ showingModal }}"
|
||||||
|
>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<p *ngIf="selectedProviderType === providerType.Authenticator">{{'enterVerificationCodeApp' | i18n}}</p>
|
<p *ngIf="selectedProviderType === providerType.Authenticator">
|
||||||
<p *ngIf="selectedProviderType === providerType.Email">
|
{{ "enterVerificationCodeApp" | i18n }}
|
||||||
{{'enterVerificationCodeEmail' | i18n : twoFactorEmail}}
|
|
||||||
</p>
|
</p>
|
||||||
<div class="box last"
|
<p *ngIf="selectedProviderType === providerType.Email">
|
||||||
*ngIf="selectedProviderType === providerType.Email || selectedProviderType === providerType.Authenticator">
|
{{ "enterVerificationCodeEmail" | i18n: twoFactorEmail }}
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="box last"
|
||||||
|
*ngIf="
|
||||||
|
selectedProviderType === providerType.Email ||
|
||||||
|
selectedProviderType === providerType.Authenticator
|
||||||
|
"
|
||||||
|
>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="code">{{'verificationCode' | i18n}}</label>
|
<label for="code">{{ "verificationCode" | i18n }}</label>
|
||||||
<input id="code" type="text" name="Code" [(ngModel)]="token" required appAutofocus appInputVerbatim>
|
<input
|
||||||
|
id="code"
|
||||||
|
type="text"
|
||||||
|
name="Code"
|
||||||
|
[(ngModel)]="token"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||||
<p>{{'insertYubiKey' | i18n}}</p>
|
<p>{{ "insertYubiKey" | i18n }}</p>
|
||||||
<img src="../../images/yubikey.jpg" alt="">
|
<img src="../../images/yubikey.jpg" alt="" />
|
||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="code" class="sr-only">{{'verificationCode' | i18n}}</label>
|
<label for="code" class="sr-only">{{ "verificationCode" | i18n }}</label>
|
||||||
<input id="code" type="password" name="Code" [(ngModel)]="token" required appAutofocus
|
<input
|
||||||
appInputVerbatim>
|
id="code"
|
||||||
|
type="password"
|
||||||
|
name="Code"
|
||||||
|
[(ngModel)]="token"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,20 +70,24 @@
|
|||||||
<div class="box first">
|
<div class="box first">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
|
<ng-container
|
||||||
selectedProviderType === providerType.OrganizationDuo">
|
*ngIf="
|
||||||
|
selectedProviderType === providerType.Duo ||
|
||||||
|
selectedProviderType === providerType.OrganizationDuo
|
||||||
|
"
|
||||||
|
>
|
||||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
|
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
|
||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -63,25 +95,43 @@
|
|||||||
<div class="box last" *ngIf="selectedProviderType == null">
|
<div class="box last" *ngIf="selectedProviderType == null">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row">
|
<div class="box-content-row">
|
||||||
<p>{{'noTwoStepProviders' | i18n}}</p>
|
<p>{{ "noTwoStepProviders" | i18n }}</p>
|
||||||
<p>{{'noTwoStepProviders2' | i18n}}</p>
|
<p>{{ "noTwoStepProviders2" | i18n }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick *ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
|
<button
|
||||||
selectedProviderType !== providerType.OrganizationDuo">
|
type="submit"
|
||||||
<span [hidden]="form.loading"><i class="fa fa-sign-in" aria-hidden="true"></i>
|
class="btn primary block"
|
||||||
{{'continue' | i18n}}</span>
|
[disabled]="form.loading"
|
||||||
|
appBlurClick
|
||||||
|
*ngIf="
|
||||||
|
selectedProviderType != null &&
|
||||||
|
selectedProviderType !== providerType.Duo &&
|
||||||
|
selectedProviderType !== providerType.OrganizationDuo
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span [hidden]="form.loading"
|
||||||
|
><i class="fa fa-sign-in" aria-hidden="true"></i> {{ "continue" | i18n }}</span
|
||||||
|
>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-options">
|
<div class="sub-options">
|
||||||
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{'useAnotherTwoStepMethod' | i18n}}</a>
|
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{
|
||||||
<a href="#" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise" role="button"
|
"useAnotherTwoStepMethod" | i18n
|
||||||
*ngIf="selectedProviderType === providerType.Email">
|
}}</a>
|
||||||
{{'sendVerificationCodeEmailAgain' | i18n}}
|
<a
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
(click)="sendEmail(true)"
|
||||||
|
[appApiAction]="emailPromise"
|
||||||
|
role="button"
|
||||||
|
*ngIf="selectedProviderType === providerType.Email"
|
||||||
|
>
|
||||||
|
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,55 +1,69 @@
|
|||||||
import {
|
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
Component,
|
|
||||||
ViewChild,
|
|
||||||
ViewContainerRef,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import {
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
ActivatedRoute,
|
|
||||||
Router,
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { TwoFactorOptionsComponent } from './two-factor-options.component';
|
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
||||||
|
|
||||||
import { TwoFactorProviderType } from 'jslib-common/enums/twoFactorProviderType';
|
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
|
|
||||||
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib-angular/components/two-factor.component';
|
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-two-factor',
|
selector: "app-two-factor",
|
||||||
templateUrl: 'two-factor.component.html',
|
templateUrl: "two-factor.component.html",
|
||||||
})
|
})
|
||||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||||
@ViewChild('twoFactorOptions', { read: ViewContainerRef, static: true }) twoFactorOptionsModal: ViewContainerRef;
|
@ViewChild("twoFactorOptions", { read: ViewContainerRef, static: true })
|
||||||
|
twoFactorOptionsModal: ViewContainerRef;
|
||||||
|
|
||||||
showingModal = false;
|
showingModal = false;
|
||||||
|
|
||||||
constructor(authService: AuthService, router: Router,
|
constructor(
|
||||||
i18nService: I18nService, apiService: ApiService,
|
authService: AuthService,
|
||||||
platformUtilsService: PlatformUtilsService, syncService: SyncService,
|
router: Router,
|
||||||
environmentService: EnvironmentService, private modalService: ModalService,
|
i18nService: I18nService,
|
||||||
stateService: StateService, route: ActivatedRoute,
|
apiService: ApiService,
|
||||||
logService: LogService) {
|
platformUtilsService: PlatformUtilsService,
|
||||||
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService,
|
syncService: SyncService,
|
||||||
stateService, route, logService);
|
environmentService: EnvironmentService,
|
||||||
|
private modalService: ModalService,
|
||||||
|
stateService: StateService,
|
||||||
|
route: ActivatedRoute,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
authService,
|
||||||
|
router,
|
||||||
|
i18nService,
|
||||||
|
apiService,
|
||||||
|
platformUtilsService,
|
||||||
|
window,
|
||||||
|
environmentService,
|
||||||
|
stateService,
|
||||||
|
route,
|
||||||
|
logService
|
||||||
|
);
|
||||||
super.onSuccessfulLogin = () => {
|
super.onSuccessfulLogin = () => {
|
||||||
return syncService.fullSync(true);
|
return syncService.fullSync(true);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async anotherMethod() {
|
async anotherMethod() {
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(TwoFactorOptionsComponent, this.twoFactorOptionsModal);
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
|
TwoFactorOptionsComponent,
|
||||||
|
this.twoFactorOptionsModal
|
||||||
|
);
|
||||||
|
|
||||||
modal.onShown.subscribe(() => {
|
modal.onShown.subscribe(() => {
|
||||||
this.showingModal = true;
|
this.showingModal = true;
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
<form id="update-temp-password-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
<form id="update-temp-password-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<app-callout type="warning" title="{{ 'updateMasterPassword' | i18n }}">
|
<app-callout type="warning" title="{{ 'updateMasterPassword' | i18n }}">
|
||||||
{{'updateMasterPasswordWarning' | i18n}}
|
{{ "updateMasterPasswordWarning" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
<app-callout
|
||||||
|
type="info"
|
||||||
|
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||||
|
*ngIf="enforcedPolicyOptions"
|
||||||
|
>
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
@ -11,30 +15,54 @@
|
|||||||
<div class="box-content-row-flex">
|
<div class="box-content-row-flex">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">
|
<label for="masterPassword">
|
||||||
{{'masterPass' | i18n}}
|
{{ "masterPass" | i18n }}
|
||||||
<strong class="sub-label text-{{masterPasswordScoreStyle.Color}}"
|
<strong
|
||||||
*ngIf="masterPasswordScoreStyle.Text">
|
class="sub-label text-{{ masterPasswordScoreStyle.Color }}"
|
||||||
|
*ngIf="masterPasswordScoreStyle.Text"
|
||||||
|
>
|
||||||
{{ masterPasswordScoreStyle.Text }}
|
{{ masterPasswordScoreStyle.Text }}
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
<input
|
||||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
id="masterPassword"
|
||||||
[appAutofocus]="masterPassword === ''" (input)="updatePasswordStrength()"
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
appInputVerbatim>
|
name="MasterPassword"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPassword"
|
||||||
|
required
|
||||||
|
[appAutofocus]="masterPassword === ''"
|
||||||
|
(input)="updatePasswordStrength()"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword(false)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar bg-{{masterPasswordScoreStyle.Color}}" role="progressbar"
|
<div
|
||||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
class="progress-bar bg-{{ masterPasswordScoreStyle.Color }}"
|
||||||
[ngStyle]="{width: (masterPasswordScoreStyle.Width + '%')}"
|
role="progressbar"
|
||||||
attr.aria-valuenow="{{masterPasswordScoreStyle.Width}}"></div>
|
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>
|
||||||
@ -43,16 +71,32 @@
|
|||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
<input
|
||||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="masterPasswordRetype" required
|
id="masterPasswordRetype"
|
||||||
appInputVerbatim>
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPasswordRetype"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPasswordRetype"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword(true)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -61,20 +105,20 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'masterPassHintDesc' | i18n}}
|
{{ "masterPassHintDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<a (click)="logOut()" class="btn block">{{'logOut' | i18n}}</a>
|
<a (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.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 {
|
interface MasterPasswordScore {
|
||||||
Color: string;
|
Color: string;
|
||||||
@ -20,46 +20,62 @@ interface MasterPasswordScore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-update-temp-password',
|
selector: "app-update-temp-password",
|
||||||
templateUrl: 'update-temp-password.component.html',
|
templateUrl: "update-temp-password.component.html",
|
||||||
})
|
})
|
||||||
|
|
||||||
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
||||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||||
switch (this.masterPasswordScore) {
|
switch (this.masterPasswordScore) {
|
||||||
case 4:
|
case 4:
|
||||||
return {
|
return {
|
||||||
Color: 'bg-success',
|
Color: "bg-success",
|
||||||
Text: 'strong',
|
Text: "strong",
|
||||||
Width: scoreWidth,
|
Width: scoreWidth,
|
||||||
};
|
};
|
||||||
case 3:
|
case 3:
|
||||||
return {
|
return {
|
||||||
Color: 'bg-primary',
|
Color: "bg-primary",
|
||||||
Text: 'good',
|
Text: "good",
|
||||||
Width: scoreWidth,
|
Width: scoreWidth,
|
||||||
};
|
};
|
||||||
case 2:
|
case 2:
|
||||||
return {
|
return {
|
||||||
Color: 'bg-warning',
|
Color: "bg-warning",
|
||||||
Text: 'weak',
|
Text: "weak",
|
||||||
Width: scoreWidth,
|
Width: scoreWidth,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
Color: 'bg-danger',
|
Color: "bg-danger",
|
||||||
Text: 'weak',
|
Text: "weak",
|
||||||
Width: scoreWidth,
|
Width: scoreWidth,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
|
i18nService: I18nService,
|
||||||
cryptoService: CryptoService, messagingService: MessagingService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
apiService: ApiService, syncService: SyncService,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
logService: LogService, stateService: StateService) {
|
policyService: PolicyService,
|
||||||
super(i18nService, platformUtilsService, passwordGenerationService, policyService, cryptoService,
|
cryptoService: CryptoService,
|
||||||
messagingService, apiService, stateService, syncService, logService);
|
messagingService: MessagingService,
|
||||||
|
apiService: ApiService,
|
||||||
|
syncService: SyncService,
|
||||||
|
logService: LogService,
|
||||||
|
stateService: StateService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
i18nService,
|
||||||
|
platformUtilsService,
|
||||||
|
passwordGenerationService,
|
||||||
|
policyService,
|
||||||
|
cryptoService,
|
||||||
|
messagingService,
|
||||||
|
apiService,
|
||||||
|
stateService,
|
||||||
|
syncService,
|
||||||
|
logService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,46 @@
|
|||||||
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
|
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
|
||||||
{{'vaultTimeoutPolicyInEffect' | i18n : vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes}}
|
{{ "vaultTimeoutPolicyInEffect" | i18n: vaultTimeoutPolicyHours:vaultTimeoutPolicyMinutes }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
|
|
||||||
<div [formGroup]="form">
|
<div [formGroup]="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
<label for="vaultTimeout">{{ "vaultTimeout" | i18n }}</label>
|
||||||
<select id="vaultTimeout" name="VaultTimeout" formControlName="vaultTimeout" class="form-control" appAutofocus>
|
<select
|
||||||
|
id="vaultTimeout"
|
||||||
|
name="VaultTimeout"
|
||||||
|
formControlName="vaultTimeout"
|
||||||
|
class="form-control"
|
||||||
|
appAutofocus
|
||||||
|
>
|
||||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="form-text text-muted">{{'vaultTimeoutDesc' | i18n}}</small>
|
<small class="form-text text-muted">{{ "vaultTimeoutDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
|
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label for="hours">{{'hours' | i18n}}</label>
|
<label for="hours">{{ "hours" | i18n }}</label>
|
||||||
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
|
<input
|
||||||
|
id="hours"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
name="hours"
|
||||||
|
formControlName="hours"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label for="minutes">{{'minutes' | i18n}}</label>
|
<label for="minutes">{{ "minutes" | i18n }}</label>
|
||||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes" formControlName="minutes">
|
<input
|
||||||
|
id="minutes"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="59"
|
||||||
|
name="minutes"
|
||||||
|
formControlName="minutes"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"></div> <!-- Styling fix -->
|
<div class="form-group"></div>
|
||||||
|
<!-- Styling fix -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import {
|
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||||
NG_VALIDATORS,
|
|
||||||
NG_VALUE_ACCESSOR,
|
|
||||||
} from '@angular/forms';
|
|
||||||
|
|
||||||
import {
|
import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "jslib-angular/components/settings/vault-timeout-input.component";
|
||||||
VaultTimeoutInputComponent as VaultTimeoutInputComponentBase
|
|
||||||
} from 'jslib-angular/components/settings/vault-timeout-input.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-timeout-input',
|
selector: "app-vault-timeout-input",
|
||||||
templateUrl: 'vault-timeout-input.component.html',
|
templateUrl: "vault-timeout-input.component.html",
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: NG_VALUE_ACCESSOR,
|
provide: NG_VALUE_ACCESSOR,
|
||||||
@ -24,5 +19,4 @@ import {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {
|
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
|
||||||
}
|
|
||||||
|
@ -1,72 +1,71 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import {
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
RouterModule,
|
|
||||||
Routes,
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { AuthGuardService } from 'jslib-angular/services/auth-guard.service';
|
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||||
import { LockGuardService } from 'jslib-angular/services/lock-guard.service';
|
import { LockGuardService } from "jslib-angular/services/lock-guard.service";
|
||||||
import { LoginGuardService } from '../services/loginGuard.service';
|
import { LoginGuardService } from "../services/loginGuard.service";
|
||||||
|
|
||||||
import { HintComponent } from './accounts/hint.component';
|
import { HintComponent } from "./accounts/hint.component";
|
||||||
import { LockComponent } from './accounts/lock.component';
|
import { LockComponent } from "./accounts/lock.component";
|
||||||
import { LoginComponent } from './accounts/login.component';
|
import { LoginComponent } from "./accounts/login.component";
|
||||||
import { RegisterComponent } from './accounts/register.component';
|
import { RegisterComponent } from "./accounts/register.component";
|
||||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||||
import { SsoComponent } from './accounts/sso.component';
|
import { SsoComponent } from "./accounts/sso.component";
|
||||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||||
|
|
||||||
import { SendComponent } from './send/send.component';
|
import { SendComponent } from "./send/send.component";
|
||||||
|
|
||||||
import { VaultComponent } from './vault/vault.component';
|
import { VaultComponent } from "./vault/vault.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: '/vault', pathMatch: 'full' },
|
{ path: "", redirectTo: "/vault", pathMatch: "full" },
|
||||||
{
|
{
|
||||||
path: 'lock',
|
path: "lock",
|
||||||
component: LockComponent,
|
component: LockComponent,
|
||||||
canActivate: [LockGuardService],
|
canActivate: [LockGuardService],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'login',
|
path: "login",
|
||||||
component: LoginComponent,
|
component: LoginComponent,
|
||||||
canActivate: [LoginGuardService],
|
canActivate: [LoginGuardService],
|
||||||
},
|
},
|
||||||
{ path: '2fa', component: TwoFactorComponent },
|
{ path: "2fa", component: TwoFactorComponent },
|
||||||
{ path: 'register', component: RegisterComponent },
|
{ path: "register", component: RegisterComponent },
|
||||||
{
|
{
|
||||||
path: 'vault',
|
path: "vault",
|
||||||
component: VaultComponent,
|
component: VaultComponent,
|
||||||
canActivate: [AuthGuardService],
|
canActivate: [AuthGuardService],
|
||||||
},
|
},
|
||||||
{ path: 'hint', component: HintComponent },
|
{ path: "hint", component: HintComponent },
|
||||||
{ path: 'set-password', component: SetPasswordComponent },
|
{ path: "set-password", component: SetPasswordComponent },
|
||||||
{ path: 'sso', component: SsoComponent },
|
{ path: "sso", component: SsoComponent },
|
||||||
{
|
{
|
||||||
path: 'send',
|
path: "send",
|
||||||
component: SendComponent,
|
component: SendComponent,
|
||||||
canActivate: [AuthGuardService],
|
canActivate: [AuthGuardService],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'update-temp-password',
|
path: "update-temp-password",
|
||||||
component: UpdateTempPasswordComponent,
|
component: UpdateTempPasswordComponent,
|
||||||
canActivate: [AuthGuardService],
|
canActivate: [AuthGuardService],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'remove-password',
|
path: "remove-password",
|
||||||
component: RemovePasswordComponent,
|
component: RemovePasswordComponent,
|
||||||
canActivate: [AuthGuardService],
|
canActivate: [AuthGuardService],
|
||||||
data: { titleId: 'removeMasterPassword' },
|
data: { titleId: "removeMasterPassword" },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes, {
|
imports: [
|
||||||
|
RouterModule.forRoot(routes, {
|
||||||
useHash: true,
|
useHash: true,
|
||||||
/*enableTracing: true,*/
|
/*enableTracing: true,*/
|
||||||
})],
|
}),
|
||||||
|
],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {}
|
export class AppRoutingModule {}
|
||||||
|
@ -6,61 +6,57 @@ import {
|
|||||||
Type,
|
Type,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
} from '@angular/core';
|
} from "@angular/core";
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from "@angular/platform-browser";
|
||||||
import { Router } from '@angular/router';
|
import { Router } from "@angular/router";
|
||||||
import {
|
import { IndividualConfig, ToastrService } from "ngx-toastr";
|
||||||
IndividualConfig,
|
|
||||||
ToastrService,
|
|
||||||
} from 'ngx-toastr';
|
|
||||||
|
|
||||||
import { PremiumComponent } from './accounts/premium.component';
|
import { PremiumComponent } from "./accounts/premium.component";
|
||||||
import { SettingsComponent } from './accounts/settings.component';
|
import { SettingsComponent } from "./accounts/settings.component";
|
||||||
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
|
import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-history.component";
|
||||||
|
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { NotificationsService } from 'jslib-common/abstractions/notifications.service';
|
import { NotificationsService } from "jslib-common/abstractions/notifications.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||||
import { SettingsService } from 'jslib-common/abstractions/settings.service';
|
import { SettingsService } from "jslib-common/abstractions/settings.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
import { SystemService } from 'jslib-common/abstractions/system.service';
|
import { SystemService } from "jslib-common/abstractions/system.service";
|
||||||
import { TokenService } from 'jslib-common/abstractions/token.service';
|
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
|
||||||
import { CipherType } from 'jslib-common/enums/cipherType';
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
|
||||||
import { ExportComponent } from './vault/export.component';
|
import { ExportComponent } from "./vault/export.component";
|
||||||
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
|
import { FolderAddEditComponent } from "./vault/folder-add-edit.component";
|
||||||
import { PasswordGeneratorComponent } from './vault/password-generator.component';
|
import { PasswordGeneratorComponent } from "./vault/password-generator.component";
|
||||||
|
|
||||||
import { ModalRef } from 'jslib-angular/components/modal/modal.ref';
|
import { ModalRef } from "jslib-angular/components/modal/modal.ref";
|
||||||
|
|
||||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
import { MenuUpdateRequest } from 'src/main/menu.updater';
|
import { MenuUpdateRequest } from "src/main/menu.updater";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'AppComponent';
|
const BroadcasterSubscriptionId = "AppComponent";
|
||||||
const IdleTimeout = 60000 * 10; // 10 minutes
|
const IdleTimeout = 60000 * 10; // 10 minutes
|
||||||
const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
|
const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: "app-root",
|
||||||
styles: [],
|
styles: [],
|
||||||
template: `
|
template: ` <ng-template #settings></ng-template>
|
||||||
<ng-template #settings></ng-template>
|
|
||||||
<ng-template #premium></ng-template>
|
<ng-template #premium></ng-template>
|
||||||
<ng-template #passwordHistory></ng-template>
|
<ng-template #passwordHistory></ng-template>
|
||||||
<ng-template #appFolderAddEdit></ng-template>
|
<ng-template #appFolderAddEdit></ng-template>
|
||||||
@ -70,13 +66,15 @@ const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
|
|||||||
<div id="container"><router-outlet></router-outlet></div>`,
|
<div id="container"><router-outlet></router-outlet></div>`,
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
@ViewChild('settings', { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef;
|
@ViewChild("settings", { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef;
|
||||||
@ViewChild('premium', { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef;
|
@ViewChild("premium", { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef;
|
||||||
@ViewChild('passwordHistory', { read: ViewContainerRef, static: true }) passwordHistoryRef: ViewContainerRef;
|
@ViewChild("passwordHistory", { read: ViewContainerRef, static: true })
|
||||||
@ViewChild('exportVault', { read: ViewContainerRef, static: true }) exportVaultModalRef: ViewContainerRef;
|
passwordHistoryRef: ViewContainerRef;
|
||||||
@ViewChild('appFolderAddEdit', { read: ViewContainerRef, static: true })
|
@ViewChild("exportVault", { read: ViewContainerRef, static: true })
|
||||||
|
exportVaultModalRef: ViewContainerRef;
|
||||||
|
@ViewChild("appFolderAddEdit", { read: ViewContainerRef, static: true })
|
||||||
folderAddEditModalRef: ViewContainerRef;
|
folderAddEditModalRef: ViewContainerRef;
|
||||||
@ViewChild('appPasswordGenerator', { read: ViewContainerRef, static: true })
|
@ViewChild("appPasswordGenerator", { read: ViewContainerRef, static: true })
|
||||||
passwordGeneratorModalRef: ViewContainerRef;
|
passwordGeneratorModalRef: ViewContainerRef;
|
||||||
|
|
||||||
private lastActivity: number = null;
|
private lastActivity: number = null;
|
||||||
@ -84,21 +82,35 @@ export class AppComponent implements OnInit {
|
|||||||
private idleTimer: number = null;
|
private idleTimer: number = null;
|
||||||
private isIdle = false;
|
private isIdle = false;
|
||||||
|
|
||||||
constructor(private broadcasterService: BroadcasterService,
|
constructor(
|
||||||
private tokenService: TokenService, private folderService: FolderService,
|
private broadcasterService: BroadcasterService,
|
||||||
private settingsService: SettingsService, private syncService: SyncService,
|
private tokenService: TokenService,
|
||||||
private passwordGenerationService: PasswordGenerationService, private cipherService: CipherService,
|
private folderService: FolderService,
|
||||||
private authService: AuthService, private router: Router,
|
private settingsService: SettingsService,
|
||||||
private toastrService: ToastrService, private i18nService: I18nService,
|
private syncService: SyncService,
|
||||||
private sanitizer: DomSanitizer, private ngZone: NgZone,
|
private passwordGenerationService: PasswordGenerationService,
|
||||||
|
private cipherService: CipherService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private router: Router,
|
||||||
|
private toastrService: ToastrService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private sanitizer: DomSanitizer,
|
||||||
|
private ngZone: NgZone,
|
||||||
private vaultTimeoutService: VaultTimeoutService,
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
private cryptoService: CryptoService, private logService: LogService,
|
private cryptoService: CryptoService,
|
||||||
private messagingService: MessagingService, private collectionService: CollectionService,
|
private logService: LogService,
|
||||||
private searchService: SearchService, private notificationsService: NotificationsService,
|
private messagingService: MessagingService,
|
||||||
private platformUtilsService: PlatformUtilsService, private systemService: SystemService,
|
private collectionService: CollectionService,
|
||||||
private stateService: StateService, private eventService: EventService,
|
private searchService: SearchService,
|
||||||
private policyService: PolicyService, private modalService: ModalService,
|
private notificationsService: NotificationsService,
|
||||||
private keyConnectorService: KeyConnectorService) { }
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private systemService: SystemService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private eventService: EventService,
|
||||||
|
private policyService: PolicyService,
|
||||||
|
private modalService: ModalService,
|
||||||
|
private keyConnectorService: KeyConnectorService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.ngZone.runOutsideAngular(() => {
|
this.ngZone.runOutsideAngular(() => {
|
||||||
@ -117,13 +129,13 @@ export class AppComponent implements OnInit {
|
|||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
this.ngZone.run(async () => {
|
this.ngZone.run(async () => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'loggedIn':
|
case "loggedIn":
|
||||||
case 'unlocked':
|
case "unlocked":
|
||||||
this.notificationsService.updateConnection();
|
this.notificationsService.updateConnection();
|
||||||
this.updateAppMenu();
|
this.updateAppMenu();
|
||||||
this.systemService.cancelProcessReload();
|
this.systemService.cancelProcessReload();
|
||||||
break;
|
break;
|
||||||
case 'loggedOut':
|
case "loggedOut":
|
||||||
if (this.modal != null) {
|
if (this.modal != null) {
|
||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
@ -132,101 +144,127 @@ export class AppComponent implements OnInit {
|
|||||||
this.systemService.startProcessReload();
|
this.systemService.startProcessReload();
|
||||||
await this.systemService.clearPendingClipboard();
|
await this.systemService.clearPendingClipboard();
|
||||||
break;
|
break;
|
||||||
case 'authBlocked':
|
case "authBlocked":
|
||||||
this.router.navigate(['login']);
|
this.router.navigate(["login"]);
|
||||||
break;
|
break;
|
||||||
case 'logout':
|
case "logout":
|
||||||
await this.logOut(!!message.expired, message.userId);
|
await this.logOut(!!message.expired, message.userId);
|
||||||
break;
|
break;
|
||||||
case 'lockVault':
|
case "lockVault":
|
||||||
await this.vaultTimeoutService.lock(true, message.userId);
|
await this.vaultTimeoutService.lock(true, message.userId);
|
||||||
break;
|
break;
|
||||||
case 'lockAllVaults':
|
case "lockAllVaults":
|
||||||
for (const userId in this.stateService.accounts.getValue()) {
|
for (const userId in this.stateService.accounts.getValue()) {
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
await this.vaultTimeoutService.lock(true, userId);
|
await this.vaultTimeoutService.lock(true, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'locked':
|
case "locked":
|
||||||
if (this.modal != null) {
|
if (this.modal != null) {
|
||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
this.updateAppMenu();
|
this.updateAppMenu();
|
||||||
if (message.userId == null || message.userId === await this.stateService.getUserId()) {
|
if (
|
||||||
this.router.navigate(['lock']);
|
message.userId == null ||
|
||||||
|
message.userId === (await this.stateService.getUserId())
|
||||||
|
) {
|
||||||
|
this.router.navigate(["lock"]);
|
||||||
}
|
}
|
||||||
this.notificationsService.updateConnection();
|
this.notificationsService.updateConnection();
|
||||||
await this.systemService.clearPendingClipboard();
|
await this.systemService.clearPendingClipboard();
|
||||||
this.systemService.startProcessReload();
|
this.systemService.startProcessReload();
|
||||||
break;
|
break;
|
||||||
case 'reloadProcess':
|
case "reloadProcess":
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
break;
|
break;
|
||||||
case 'syncStarted':
|
case "syncStarted":
|
||||||
break;
|
break;
|
||||||
case 'syncCompleted':
|
case "syncCompleted":
|
||||||
await this.updateAppMenu();
|
await this.updateAppMenu();
|
||||||
break;
|
break;
|
||||||
case 'openSettings':
|
case "openSettings":
|
||||||
await this.openModal<SettingsComponent>(SettingsComponent, this.settingsRef);
|
await this.openModal<SettingsComponent>(SettingsComponent, this.settingsRef);
|
||||||
break;
|
break;
|
||||||
case 'openPremium':
|
case "openPremium":
|
||||||
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
|
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
|
||||||
break;
|
break;
|
||||||
case 'showFingerprintPhrase':
|
case "showFingerprintPhrase":
|
||||||
const fingerprint = await this.cryptoService.getFingerprint(
|
const fingerprint = await this.cryptoService.getFingerprint(
|
||||||
await this.stateService.getUserId());
|
await this.stateService.getUserId()
|
||||||
|
);
|
||||||
const result = await this.platformUtilsService.showDialog(
|
const result = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('yourAccountsFingerprint') + ':\n' + fingerprint.join('-'),
|
this.i18nService.t("yourAccountsFingerprint") + ":\n" + fingerprint.join("-"),
|
||||||
this.i18nService.t('fingerprintPhrase'), this.i18nService.t('learnMore'),
|
this.i18nService.t("fingerprintPhrase"),
|
||||||
this.i18nService.t('close'));
|
this.i18nService.t("learnMore"),
|
||||||
|
this.i18nService.t("close")
|
||||||
|
);
|
||||||
if (result) {
|
if (result) {
|
||||||
this.platformUtilsService.launchUri(
|
this.platformUtilsService.launchUri(
|
||||||
'https://help.bitwarden.com/article/fingerprint-phrase/');
|
"https://help.bitwarden.com/article/fingerprint-phrase/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'openPasswordHistory':
|
case "openPasswordHistory":
|
||||||
await this.openModal<PasswordGeneratorHistoryComponent>(
|
await this.openModal<PasswordGeneratorHistoryComponent>(
|
||||||
PasswordGeneratorHistoryComponent, this.passwordHistoryRef);
|
PasswordGeneratorHistoryComponent,
|
||||||
|
this.passwordHistoryRef
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 'showToast':
|
case "showToast":
|
||||||
this.showToast(message);
|
this.showToast(message);
|
||||||
break;
|
break;
|
||||||
case 'copiedToClipboard':
|
case "copiedToClipboard":
|
||||||
if (!message.clearing) {
|
if (!message.clearing) {
|
||||||
this.systemService.clearClipboard(message.clipboardValue, message.clearMs);
|
this.systemService.clearClipboard(message.clipboardValue, message.clearMs);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'ssoCallback':
|
case "ssoCallback":
|
||||||
this.router.navigate(['sso'], { queryParams: { code: message.code, state: message.state } });
|
this.router.navigate(["sso"], {
|
||||||
|
queryParams: { code: message.code, state: message.state },
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'premiumRequired':
|
case "premiumRequired":
|
||||||
const premiumConfirmed = await this.platformUtilsService.showDialog(
|
const premiumConfirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('premiumRequiredDesc'), this.i18nService.t('premiumRequired'),
|
this.i18nService.t("premiumRequiredDesc"),
|
||||||
this.i18nService.t('learnMore'), this.i18nService.t('cancel'));
|
this.i18nService.t("premiumRequired"),
|
||||||
|
this.i18nService.t("learnMore"),
|
||||||
|
this.i18nService.t("cancel")
|
||||||
|
);
|
||||||
if (premiumConfirmed) {
|
if (premiumConfirmed) {
|
||||||
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
|
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'emailVerificationRequired':
|
case "emailVerificationRequired":
|
||||||
const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
|
const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('emailVerificationRequiredDesc'),
|
this.i18nService.t("emailVerificationRequiredDesc"),
|
||||||
this.i18nService.t('emailVerificationRequired'),
|
this.i18nService.t("emailVerificationRequired"),
|
||||||
this.i18nService.t('learnMore'), this.i18nService.t('cancel'));
|
this.i18nService.t("learnMore"),
|
||||||
|
this.i18nService.t("cancel")
|
||||||
|
);
|
||||||
if (emailVerificationConfirmed) {
|
if (emailVerificationConfirmed) {
|
||||||
this.platformUtilsService.launchUri('https://bitwarden.com/help/article/create-bitwarden-account/');
|
this.platformUtilsService.launchUri(
|
||||||
|
"https://bitwarden.com/help/article/create-bitwarden-account/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'syncVault':
|
case "syncVault":
|
||||||
try {
|
try {
|
||||||
await this.syncService.fullSync(true, true);
|
await this.syncService.fullSync(true, true);
|
||||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('syncingComplete'));
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("syncingComplete")
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('syncingFailed'));
|
this.platformUtilsService.showToast(
|
||||||
|
"error",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("syncingFailed")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'checkSyncVault':
|
case "checkSyncVault":
|
||||||
try {
|
try {
|
||||||
const lastSync = await this.syncService.getLastSync();
|
const lastSync = await this.syncService.getLastSync();
|
||||||
let lastSyncAgo = SyncInterval + 1;
|
let lastSyncAgo = SyncInterval + 1;
|
||||||
@ -240,37 +278,37 @@ export class AppComponent implements OnInit {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
this.messagingService.send('scheduleNextSync');
|
this.messagingService.send("scheduleNextSync");
|
||||||
break;
|
break;
|
||||||
case 'exportVault':
|
case "exportVault":
|
||||||
await this.openExportVault();
|
await this.openExportVault();
|
||||||
break;
|
break;
|
||||||
case 'newLogin':
|
case "newLogin":
|
||||||
this.routeToVault('add', CipherType.Login);
|
this.routeToVault("add", CipherType.Login);
|
||||||
break;
|
break;
|
||||||
case 'newCard':
|
case "newCard":
|
||||||
this.routeToVault('add', CipherType.Card);
|
this.routeToVault("add", CipherType.Card);
|
||||||
break;
|
break;
|
||||||
case 'newIdentity':
|
case "newIdentity":
|
||||||
this.routeToVault('add', CipherType.Identity);
|
this.routeToVault("add", CipherType.Identity);
|
||||||
break;
|
break;
|
||||||
case 'newSecureNote':
|
case "newSecureNote":
|
||||||
this.routeToVault('add', CipherType.SecureNote);
|
this.routeToVault("add", CipherType.SecureNote);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
case 'newFolder':
|
case "newFolder":
|
||||||
await this.addFolder();
|
await this.addFolder();
|
||||||
break;
|
break;
|
||||||
case 'openPasswordGenerator':
|
case "openPasswordGenerator":
|
||||||
// openPasswordGenerator has extended functionality if called in the vault
|
// openPasswordGenerator has extended functionality if called in the vault
|
||||||
if (!this.router.url.includes('vault')) {
|
if (!this.router.url.includes("vault")) {
|
||||||
await this.openPasswordGenerator();
|
await this.openPasswordGenerator();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'convertAccountToKeyConnector':
|
case "convertAccountToKeyConnector":
|
||||||
await this.keyConnectorService.setConvertAccountRequired(true);
|
await this.keyConnectorService.setConvertAccountRequired(true);
|
||||||
this.router.navigate(['/remove-password']);
|
this.router.navigate(["/remove-password"]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -286,7 +324,10 @@ export class AppComponent implements OnInit {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(ExportComponent, this.exportVaultModalRef);
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
|
ExportComponent,
|
||||||
|
this.exportVaultModalRef
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSaved.subscribe(() => {
|
childComponent.onSaved.subscribe(() => {
|
||||||
@ -303,8 +344,11 @@ export class AppComponent implements OnInit {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(FolderAddEditComponent,
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
this.folderAddEditModalRef, comp => comp.folderId = null);
|
FolderAddEditComponent,
|
||||||
|
this.folderAddEditModalRef,
|
||||||
|
(comp) => (comp.folderId = null)
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSavedFolder.subscribe(async () => {
|
childComponent.onSavedFolder.subscribe(async () => {
|
||||||
@ -322,8 +366,11 @@ export class AppComponent implements OnInit {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
[this.modal] = await this.modalService.openViewRef(PasswordGeneratorComponent, this.folderAddEditModalRef,
|
[this.modal] = await this.modalService.openViewRef(
|
||||||
comp => comp.showSelect = false);
|
PasswordGeneratorComponent,
|
||||||
|
this.folderAddEditModalRef,
|
||||||
|
(comp) => (comp.showSelect = false)
|
||||||
|
);
|
||||||
|
|
||||||
this.modal.onClosed.subscribe(() => {
|
this.modal.onClosed.subscribe(() => {
|
||||||
this.modal = null;
|
this.modal = null;
|
||||||
@ -361,7 +408,7 @@ export class AppComponent implements OnInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messagingService.send('updateAppMenu', { updateRequest: updateRequest });
|
this.messagingService.send("updateAppMenu", { updateRequest: updateRequest });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async logOut(expired: boolean, userId?: string) {
|
private async logOut(expired: boolean, userId?: string) {
|
||||||
@ -382,12 +429,15 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
await this.stateService.setBiometricLocked(true, { userId: userId });
|
await this.stateService.setBiometricLocked(true, { userId: userId });
|
||||||
|
|
||||||
if (userId == null || userId === await this.stateService.getUserId()) {
|
if (userId == null || userId === (await this.stateService.getUserId())) {
|
||||||
this.searchService.clearIndex();
|
this.searchService.clearIndex();
|
||||||
this.authService.logOut(async () => {
|
this.authService.logOut(async () => {
|
||||||
if (expired) {
|
if (expired) {
|
||||||
this.platformUtilsService.showToast('warning', this.i18nService.t('loggedOut'),
|
this.platformUtilsService.showToast(
|
||||||
this.i18nService.t('loginExpired'));
|
"warning",
|
||||||
|
this.i18nService.t("loggedOut"),
|
||||||
|
this.i18nService.t("loginExpired")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -395,15 +445,15 @@ export class AppComponent implements OnInit {
|
|||||||
await this.stateService.clean({ userId: userId });
|
await this.stateService.clean({ userId: userId });
|
||||||
|
|
||||||
if (this.stateService.activeAccount.getValue() == null) {
|
if (this.stateService.activeAccount.getValue() == null) {
|
||||||
this.router.navigate(['login']);
|
this.router.navigate(["login"]);
|
||||||
} else {
|
} else {
|
||||||
const locked = await this.vaultTimeoutService.isLocked();
|
const locked = await this.vaultTimeoutService.isLocked();
|
||||||
if (locked) {
|
if (locked) {
|
||||||
this.messagingService.send('locked');
|
this.messagingService.send("locked");
|
||||||
} else {
|
} else {
|
||||||
this.messagingService.send('unlocked');
|
this.messagingService.send("unlocked");
|
||||||
this.messagingService.send('syncVault');
|
this.messagingService.send("syncVault");
|
||||||
this.router.navigate(['vault']);
|
this.router.navigate(["vault"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +461,7 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async recordActivity() {
|
private async recordActivity() {
|
||||||
const now = (new Date()).getTime();
|
const now = new Date().getTime();
|
||||||
if (this.lastActivity != null && now - this.lastActivity < 250) {
|
if (this.lastActivity != null && now - this.lastActivity < 250) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -457,17 +507,19 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private showToast(msg: any) {
|
private showToast(msg: any) {
|
||||||
let message = '';
|
let message = "";
|
||||||
|
|
||||||
const options: Partial<IndividualConfig> = {};
|
const options: Partial<IndividualConfig> = {};
|
||||||
|
|
||||||
if (typeof (msg.text) === 'string') {
|
if (typeof msg.text === "string") {
|
||||||
message = msg.text;
|
message = msg.text;
|
||||||
} else if (msg.text.length === 1) {
|
} else if (msg.text.length === 1) {
|
||||||
message = msg.text[0];
|
message = msg.text[0];
|
||||||
} else {
|
} else {
|
||||||
msg.text.forEach((t: string) =>
|
msg.text.forEach(
|
||||||
message += ('<p>' + this.sanitizer.sanitize(SecurityContext.HTML, t) + '</p>'));
|
(t: string) =>
|
||||||
|
(message += "<p>" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "</p>")
|
||||||
|
);
|
||||||
options.enableHtml = true;
|
options.enableHtml = true;
|
||||||
}
|
}
|
||||||
if (msg.options != null) {
|
if (msg.options != null) {
|
||||||
@ -479,12 +531,12 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toastrService.show(message, msg.title, options, 'toast-' + msg.type);
|
this.toastrService.show(message, msg.title, options, "toast-" + msg.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private routeToVault(action: string, cipherType: CipherType) {
|
private routeToVault(action: string, cipherType: CipherType) {
|
||||||
if (!this.router.url.includes('vault')) {
|
if (!this.router.url.includes("vault")) {
|
||||||
this.router.navigate(['/vault'], {
|
this.router.navigate(["/vault"], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
action: action,
|
action: action,
|
||||||
addType: cipherType,
|
addType: cipherType,
|
||||||
|
@ -1,177 +1,177 @@
|
|||||||
import 'zone.js/dist/zone';
|
import "zone.js/dist/zone";
|
||||||
|
|
||||||
import { A11yModule } from '@angular/cdk/a11y';
|
import { A11yModule } from "@angular/cdk/a11y";
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||||
import { OverlayModule } from '@angular/cdk/overlay';
|
import { OverlayModule } from "@angular/cdk/overlay";
|
||||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from "@angular/common";
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from "@angular/platform-browser";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import 'zone.js/dist/zone';
|
import "zone.js/dist/zone";
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import { ServicesModule } from './services.module';
|
import { ServicesModule } from "./services.module";
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from "./app.component";
|
||||||
|
|
||||||
import { EnvironmentComponent } from './accounts/environment.component';
|
import { EnvironmentComponent } from "./accounts/environment.component";
|
||||||
import { HintComponent } from './accounts/hint.component';
|
import { HintComponent } from "./accounts/hint.component";
|
||||||
import { LockComponent } from './accounts/lock.component';
|
import { LockComponent } from "./accounts/lock.component";
|
||||||
import { LoginComponent } from './accounts/login.component';
|
import { LoginComponent } from "./accounts/login.component";
|
||||||
import { PremiumComponent } from './accounts/premium.component';
|
import { PremiumComponent } from "./accounts/premium.component";
|
||||||
import { RegisterComponent } from './accounts/register.component';
|
import { RegisterComponent } from "./accounts/register.component";
|
||||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||||
import { SettingsComponent } from './accounts/settings.component';
|
import { SettingsComponent } from "./accounts/settings.component";
|
||||||
import { SsoComponent } from './accounts/sso.component';
|
import { SsoComponent } from "./accounts/sso.component";
|
||||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
import { TwoFactorOptionsComponent } from "./accounts/two-factor-options.component";
|
||||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||||
import { VaultTimeoutInputComponent } from './accounts/vault-timeout-input.component';
|
import { VaultTimeoutInputComponent } from "./accounts/vault-timeout-input.component";
|
||||||
|
|
||||||
import { AvatarComponent } from 'jslib-angular/components/avatar.component';
|
import { AvatarComponent } from "jslib-angular/components/avatar.component";
|
||||||
import { CalloutComponent } from 'jslib-angular/components/callout.component';
|
import { CalloutComponent } from "jslib-angular/components/callout.component";
|
||||||
import { IconComponent } from 'jslib-angular/components/icon.component';
|
import { IconComponent } from "jslib-angular/components/icon.component";
|
||||||
import { BitwardenToastModule } from 'jslib-angular/components/toastr.component';
|
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
||||||
|
|
||||||
import { A11yTitleDirective } from 'jslib-angular/directives/a11y-title.directive';
|
import { A11yTitleDirective } from "jslib-angular/directives/a11y-title.directive";
|
||||||
import { ApiActionDirective } from 'jslib-angular/directives/api-action.directive';
|
import { ApiActionDirective } from "jslib-angular/directives/api-action.directive";
|
||||||
import { AutofocusDirective } from 'jslib-angular/directives/autofocus.directive';
|
import { AutofocusDirective } from "jslib-angular/directives/autofocus.directive";
|
||||||
import { BlurClickDirective } from 'jslib-angular/directives/blur-click.directive';
|
import { BlurClickDirective } from "jslib-angular/directives/blur-click.directive";
|
||||||
import { BoxRowDirective } from 'jslib-angular/directives/box-row.directive';
|
import { BoxRowDirective } from "jslib-angular/directives/box-row.directive";
|
||||||
import { CipherListVirtualScroll } from 'jslib-angular/directives/cipherListVirtualScroll.directive';
|
import { CipherListVirtualScroll } from "jslib-angular/directives/cipherListVirtualScroll.directive";
|
||||||
import { FallbackSrcDirective } from 'jslib-angular/directives/fallback-src.directive';
|
import { FallbackSrcDirective } from "jslib-angular/directives/fallback-src.directive";
|
||||||
import { InputVerbatimDirective } from 'jslib-angular/directives/input-verbatim.directive';
|
import { InputVerbatimDirective } from "jslib-angular/directives/input-verbatim.directive";
|
||||||
import { SelectCopyDirective } from 'jslib-angular/directives/select-copy.directive';
|
import { SelectCopyDirective } from "jslib-angular/directives/select-copy.directive";
|
||||||
import { StopClickDirective } from 'jslib-angular/directives/stop-click.directive';
|
import { StopClickDirective } from "jslib-angular/directives/stop-click.directive";
|
||||||
import { StopPropDirective } from 'jslib-angular/directives/stop-prop.directive';
|
import { StopPropDirective } from "jslib-angular/directives/stop-prop.directive";
|
||||||
import { TrueFalseValueDirective } from 'jslib-angular/directives/true-false-value.directive';
|
import { TrueFalseValueDirective } from "jslib-angular/directives/true-false-value.directive";
|
||||||
|
|
||||||
import { ColorPasswordPipe } from 'jslib-angular/pipes/color-password.pipe';
|
import { ColorPasswordPipe } from "jslib-angular/pipes/color-password.pipe";
|
||||||
import { I18nPipe } from 'jslib-angular/pipes/i18n.pipe';
|
import { I18nPipe } from "jslib-angular/pipes/i18n.pipe";
|
||||||
import { SearchCiphersPipe } from 'jslib-angular/pipes/search-ciphers.pipe';
|
import { SearchCiphersPipe } from "jslib-angular/pipes/search-ciphers.pipe";
|
||||||
|
|
||||||
import { AddEditCustomFieldsComponent } from './vault/add-edit-custom-fields.component';
|
import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component";
|
||||||
import { AddEditComponent } from './vault/add-edit.component';
|
import { AddEditComponent } from "./vault/add-edit.component";
|
||||||
import { AttachmentsComponent } from './vault/attachments.component';
|
import { AttachmentsComponent } from "./vault/attachments.component";
|
||||||
import { CiphersComponent } from './vault/ciphers.component';
|
import { CiphersComponent } from "./vault/ciphers.component";
|
||||||
import { CollectionsComponent } from './vault/collections.component';
|
import { CollectionsComponent } from "./vault/collections.component";
|
||||||
import { ExportComponent } from './vault/export.component';
|
import { ExportComponent } from "./vault/export.component";
|
||||||
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
|
import { FolderAddEditComponent } from "./vault/folder-add-edit.component";
|
||||||
import { GroupingsComponent } from './vault/groupings.component';
|
import { GroupingsComponent } from "./vault/groupings.component";
|
||||||
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
|
import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-history.component";
|
||||||
import { PasswordGeneratorComponent } from './vault/password-generator.component';
|
import { PasswordGeneratorComponent } from "./vault/password-generator.component";
|
||||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
import { PasswordHistoryComponent } from "./vault/password-history.component";
|
||||||
import { ShareComponent } from './vault/share.component';
|
import { ShareComponent } from "./vault/share.component";
|
||||||
import { VaultComponent } from './vault/vault.component';
|
import { VaultComponent } from "./vault/vault.component";
|
||||||
import { ViewCustomFieldsComponent } from './vault/view-custom-fields.component';
|
import { ViewCustomFieldsComponent } from "./vault/view-custom-fields.component";
|
||||||
import { ViewComponent } from './vault/view.component';
|
import { ViewComponent } from "./vault/view.component";
|
||||||
|
|
||||||
import { AddEditComponent as SendAddEditComponent } from './send/add-edit.component';
|
import { AddEditComponent as SendAddEditComponent } from "./send/add-edit.component";
|
||||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from './send/efflux-dates.component';
|
import { EffluxDatesComponent as SendEffluxDatesComponent } from "./send/efflux-dates.component";
|
||||||
import { SendComponent } from './send/send.component';
|
import { SendComponent } from "./send/send.component";
|
||||||
|
|
||||||
import { AccountSwitcherComponent } from './layout/account-switcher.component';
|
import { AccountSwitcherComponent } from "./layout/account-switcher.component";
|
||||||
import { HeaderComponent } from './layout/header.component';
|
import { HeaderComponent } from "./layout/header.component";
|
||||||
import { NavComponent } from './layout/nav.component';
|
import { NavComponent } from "./layout/nav.component";
|
||||||
import { SearchComponent } from './layout/search/search.component';
|
import { SearchComponent } from "./layout/search/search.component";
|
||||||
|
|
||||||
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
import { PasswordRepromptComponent } from "./components/password-reprompt.component";
|
||||||
import { SetPinComponent } from './components/set-pin.component';
|
import { SetPinComponent } from "./components/set-pin.component";
|
||||||
import { VerifyMasterPasswordComponent } from './components/verify-master-password.component';
|
import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component";
|
||||||
|
|
||||||
import { registerLocaleData } from '@angular/common';
|
import { registerLocaleData } from "@angular/common";
|
||||||
import localeAf from '@angular/common/locales/af';
|
import localeAf from "@angular/common/locales/af";
|
||||||
import localeAz from '@angular/common/locales/az';
|
import localeAz from "@angular/common/locales/az";
|
||||||
import localeBe from '@angular/common/locales/be';
|
import localeBe from "@angular/common/locales/be";
|
||||||
import localeBg from '@angular/common/locales/bg';
|
import localeBg from "@angular/common/locales/bg";
|
||||||
import localeBn from '@angular/common/locales/bn';
|
import localeBn from "@angular/common/locales/bn";
|
||||||
import localeCa from '@angular/common/locales/ca';
|
import localeCa from "@angular/common/locales/ca";
|
||||||
import localeCs from '@angular/common/locales/cs';
|
import localeCs from "@angular/common/locales/cs";
|
||||||
import localeDa from '@angular/common/locales/da';
|
import localeDa from "@angular/common/locales/da";
|
||||||
import localeDe from '@angular/common/locales/de';
|
import localeDe from "@angular/common/locales/de";
|
||||||
import localeEl from '@angular/common/locales/el';
|
import localeEl from "@angular/common/locales/el";
|
||||||
import localeEnGb from '@angular/common/locales/en-GB';
|
import localeEnGb from "@angular/common/locales/en-GB";
|
||||||
import localeEnIn from '@angular/common/locales/en-IN';
|
import localeEnIn from "@angular/common/locales/en-IN";
|
||||||
import localeEs from '@angular/common/locales/es';
|
import localeEs from "@angular/common/locales/es";
|
||||||
import localeEt from '@angular/common/locales/et';
|
import localeEt from "@angular/common/locales/et";
|
||||||
import localeFa from '@angular/common/locales/fa';
|
import localeFa from "@angular/common/locales/fa";
|
||||||
import localeFi from '@angular/common/locales/fi';
|
import localeFi from "@angular/common/locales/fi";
|
||||||
import localeFr from '@angular/common/locales/fr';
|
import localeFr from "@angular/common/locales/fr";
|
||||||
import localeHe from '@angular/common/locales/he';
|
import localeHe from "@angular/common/locales/he";
|
||||||
import localeHr from '@angular/common/locales/hr';
|
import localeHr from "@angular/common/locales/hr";
|
||||||
import localeHu from '@angular/common/locales/hu';
|
import localeHu from "@angular/common/locales/hu";
|
||||||
import localeId from '@angular/common/locales/id';
|
import localeId from "@angular/common/locales/id";
|
||||||
import localeIt from '@angular/common/locales/it';
|
import localeIt from "@angular/common/locales/it";
|
||||||
import localeJa from '@angular/common/locales/ja';
|
import localeJa from "@angular/common/locales/ja";
|
||||||
import localeKn from '@angular/common/locales/kn';
|
import localeKn from "@angular/common/locales/kn";
|
||||||
import localeKo from '@angular/common/locales/ko';
|
import localeKo from "@angular/common/locales/ko";
|
||||||
import localeLv from '@angular/common/locales/lv';
|
import localeLv from "@angular/common/locales/lv";
|
||||||
import localeMl from '@angular/common/locales/ml';
|
import localeMl from "@angular/common/locales/ml";
|
||||||
import localeNb from '@angular/common/locales/nb';
|
import localeNb from "@angular/common/locales/nb";
|
||||||
import localeNl from '@angular/common/locales/nl';
|
import localeNl from "@angular/common/locales/nl";
|
||||||
import localePl from '@angular/common/locales/pl';
|
import localePl from "@angular/common/locales/pl";
|
||||||
import localePtBr from '@angular/common/locales/pt';
|
import localePtBr from "@angular/common/locales/pt";
|
||||||
import localePtPt from '@angular/common/locales/pt-PT';
|
import localePtPt from "@angular/common/locales/pt-PT";
|
||||||
import localeRo from '@angular/common/locales/ro';
|
import localeRo from "@angular/common/locales/ro";
|
||||||
import localeRu from '@angular/common/locales/ru';
|
import localeRu from "@angular/common/locales/ru";
|
||||||
import localeSk from '@angular/common/locales/sk';
|
import localeSk from "@angular/common/locales/sk";
|
||||||
import localeSr from '@angular/common/locales/sr';
|
import localeSr from "@angular/common/locales/sr";
|
||||||
import localeMe from '@angular/common/locales/sr-Latn-ME';
|
import localeMe from "@angular/common/locales/sr-Latn-ME";
|
||||||
import localeSv from '@angular/common/locales/sv';
|
import localeSv from "@angular/common/locales/sv";
|
||||||
import localeTh from '@angular/common/locales/th';
|
import localeTh from "@angular/common/locales/th";
|
||||||
import localeTr from '@angular/common/locales/tr';
|
import localeTr from "@angular/common/locales/tr";
|
||||||
import localeUk from '@angular/common/locales/uk';
|
import localeUk from "@angular/common/locales/uk";
|
||||||
import localeVi from '@angular/common/locales/vi';
|
import localeVi from "@angular/common/locales/vi";
|
||||||
import localeZhCn from '@angular/common/locales/zh-Hans';
|
import localeZhCn from "@angular/common/locales/zh-Hans";
|
||||||
import localeZhTw from '@angular/common/locales/zh-Hant';
|
import localeZhTw from "@angular/common/locales/zh-Hant";
|
||||||
|
|
||||||
registerLocaleData(localeAf, 'af');
|
registerLocaleData(localeAf, "af");
|
||||||
registerLocaleData(localeAz, 'az');
|
registerLocaleData(localeAz, "az");
|
||||||
registerLocaleData(localeBe, 'be');
|
registerLocaleData(localeBe, "be");
|
||||||
registerLocaleData(localeBg, 'bg');
|
registerLocaleData(localeBg, "bg");
|
||||||
registerLocaleData(localeBn, 'bn');
|
registerLocaleData(localeBn, "bn");
|
||||||
registerLocaleData(localeCa, 'ca');
|
registerLocaleData(localeCa, "ca");
|
||||||
registerLocaleData(localeCs, 'cs');
|
registerLocaleData(localeCs, "cs");
|
||||||
registerLocaleData(localeDa, 'da');
|
registerLocaleData(localeDa, "da");
|
||||||
registerLocaleData(localeDe, 'de');
|
registerLocaleData(localeDe, "de");
|
||||||
registerLocaleData(localeEl, 'el');
|
registerLocaleData(localeEl, "el");
|
||||||
registerLocaleData(localeEnGb, 'en-GB');
|
registerLocaleData(localeEnGb, "en-GB");
|
||||||
registerLocaleData(localeEnIn, 'en-IN');
|
registerLocaleData(localeEnIn, "en-IN");
|
||||||
registerLocaleData(localeEs, 'es');
|
registerLocaleData(localeEs, "es");
|
||||||
registerLocaleData(localeEt, 'et');
|
registerLocaleData(localeEt, "et");
|
||||||
registerLocaleData(localeFa, 'fa');
|
registerLocaleData(localeFa, "fa");
|
||||||
registerLocaleData(localeFi, 'fi');
|
registerLocaleData(localeFi, "fi");
|
||||||
registerLocaleData(localeFr, 'fr');
|
registerLocaleData(localeFr, "fr");
|
||||||
registerLocaleData(localeHe, 'he');
|
registerLocaleData(localeHe, "he");
|
||||||
registerLocaleData(localeHr, 'hr');
|
registerLocaleData(localeHr, "hr");
|
||||||
registerLocaleData(localeHu, 'hu');
|
registerLocaleData(localeHu, "hu");
|
||||||
registerLocaleData(localeId, 'id');
|
registerLocaleData(localeId, "id");
|
||||||
registerLocaleData(localeIt, 'it');
|
registerLocaleData(localeIt, "it");
|
||||||
registerLocaleData(localeJa, 'ja');
|
registerLocaleData(localeJa, "ja");
|
||||||
registerLocaleData(localeKn, 'kn');
|
registerLocaleData(localeKn, "kn");
|
||||||
registerLocaleData(localeKo, 'ko');
|
registerLocaleData(localeKo, "ko");
|
||||||
registerLocaleData(localeLv, 'lv');
|
registerLocaleData(localeLv, "lv");
|
||||||
registerLocaleData(localeMe, 'me');
|
registerLocaleData(localeMe, "me");
|
||||||
registerLocaleData(localeMl, 'ml');
|
registerLocaleData(localeMl, "ml");
|
||||||
registerLocaleData(localeNb, 'nb');
|
registerLocaleData(localeNb, "nb");
|
||||||
registerLocaleData(localeNl, 'nl');
|
registerLocaleData(localeNl, "nl");
|
||||||
registerLocaleData(localePl, 'pl');
|
registerLocaleData(localePl, "pl");
|
||||||
registerLocaleData(localePtBr, 'pt-BR');
|
registerLocaleData(localePtBr, "pt-BR");
|
||||||
registerLocaleData(localePtPt, 'pt-PT');
|
registerLocaleData(localePtPt, "pt-PT");
|
||||||
registerLocaleData(localeRo, 'ro');
|
registerLocaleData(localeRo, "ro");
|
||||||
registerLocaleData(localeRu, 'ru');
|
registerLocaleData(localeRu, "ru");
|
||||||
registerLocaleData(localeSk, 'sk');
|
registerLocaleData(localeSk, "sk");
|
||||||
registerLocaleData(localeSr, 'sr');
|
registerLocaleData(localeSr, "sr");
|
||||||
registerLocaleData(localeSv, 'sv');
|
registerLocaleData(localeSv, "sv");
|
||||||
registerLocaleData(localeTh, 'th');
|
registerLocaleData(localeTh, "th");
|
||||||
registerLocaleData(localeTr, 'tr');
|
registerLocaleData(localeTr, "tr");
|
||||||
registerLocaleData(localeUk, 'uk');
|
registerLocaleData(localeUk, "uk");
|
||||||
registerLocaleData(localeVi, 'vi');
|
registerLocaleData(localeVi, "vi");
|
||||||
registerLocaleData(localeZhCn, 'zh-CN');
|
registerLocaleData(localeZhCn, "zh-CN");
|
||||||
registerLocaleData(localeZhTw, 'zh-TW');
|
registerLocaleData(localeZhTw, "zh-TW");
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -3,34 +3,51 @@
|
|||||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">{{'passwordConfirmation' | i18n}}</div>
|
<div class="box-header">{{ "passwordConfirmation" | i18n }}</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
<input
|
||||||
class="monospaced" [(ngModel)]="masterPassword" required appAutofocus>
|
id="masterPassword"
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="MasterPassword"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="masterPassword"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'passwordConfirmationDesc' | i18n}}
|
{{ "passwordConfirmationDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||||
<span>{{'ok' | i18n}}</span>
|
<span>{{ "ok" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||||
{{'cancel' | i18n}}
|
{{ "cancel" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -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({
|
@Component({
|
||||||
templateUrl: 'password-reprompt.component.html',
|
templateUrl: "password-reprompt.component.html",
|
||||||
})
|
})
|
||||||
export class PasswordRepromptComponent extends BasePasswordRepromptComponent {}
|
export class PasswordRepromptComponent extends BasePasswordRepromptComponent {}
|
||||||
|
@ -3,21 +3,38 @@
|
|||||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>
|
<div>
|
||||||
{{'setYourPinCode' | i18n}}
|
{{ "setYourPinCode" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="pin">{{'pin' | i18n}}</label>
|
<label for="pin">{{ "pin" | i18n }}</label>
|
||||||
<input id="pin" type="{{showPin ? 'text' : 'password'}}" name="Pin" class="monospaced"
|
<input
|
||||||
[(ngModel)]="pin" required appInputVerbatim appAutofocus>
|
id="pin"
|
||||||
|
type="{{ showPin ? 'text' : 'password' }}"
|
||||||
|
name="Pin"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="pin"
|
||||||
|
required
|
||||||
|
appInputVerbatim
|
||||||
|
appAutofocus
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleVisibility()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPin, 'fa-eye-slash': showPin}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="toggleVisibility()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPin, 'fa-eye-slash': showPin }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -25,18 +42,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
||||||
<label for="masterPasswordOnRestart">
|
<label for="masterPasswordOnRestart">
|
||||||
<input type="checkbox" id="masterPasswordOnRestart" name="MasterPasswordOnRestart"
|
<input
|
||||||
[(ngModel)]="masterPassOnRestart">
|
type="checkbox"
|
||||||
<span>{{'lockWithMasterPassOnRestart' | i18n}}</span>
|
id="masterPasswordOnRestart"
|
||||||
|
name="MasterPasswordOnRestart"
|
||||||
|
[(ngModel)]="masterPassOnRestart"
|
||||||
|
/>
|
||||||
|
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||||
<span>{{'ok' | i18n}}</span>
|
<span>{{ "ok" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||||
{{'cancel' | i18n}}
|
{{ "cancel" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -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({
|
@Component({
|
||||||
templateUrl: 'set-pin.component.html',
|
templateUrl: "set-pin.component.html",
|
||||||
})
|
})
|
||||||
export class SetPinComponent extends BaseSetPinComponent {}
|
export class SetPinComponent extends BaseSetPinComponent {}
|
||||||
|
@ -1,25 +1,46 @@
|
|||||||
<ng-container *ngIf="!usesKeyConnector">
|
<ng-container *ngIf="!usesKeyConnector">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control"
|
<input
|
||||||
[formControl]="secret" required appAutofocus appInputVerbatim>
|
id="masterPassword"
|
||||||
|
type="password"
|
||||||
|
name="MasterPasswordHash"
|
||||||
|
class="form-control"
|
||||||
|
[formControl]="secret"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="usesKeyConnector">
|
<ng-container *ngIf="usesKeyConnector">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label class="d-block">{{'sendVerificationCode' | i18n}}</label>
|
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
|
||||||
<button type="button" class="btn btn-outline-secondary" (click)="requestOTP()" [disabled]="disableRequestOTP">
|
<button
|
||||||
{{'sendCode' | i18n}}
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
(click)="requestOTP()"
|
||||||
|
[disabled]="disableRequestOTP"
|
||||||
|
>
|
||||||
|
{{ "sendCode" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||||
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
|
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
|
||||||
{{'codeSent' | i18n}}
|
{{ "codeSent" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="verificationCode">{{'verificationCode' | i18n}}</label>
|
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
|
||||||
<input id="verificationCode" type="input" name="verificationCode" class="form-control"
|
<input
|
||||||
[formControl]="secret" required appAutofocus appInputVerbatim>
|
id="verificationCode"
|
||||||
|
type="input"
|
||||||
|
name="verificationCode"
|
||||||
|
class="form-control"
|
||||||
|
[formControl]="secret"
|
||||||
|
required
|
||||||
|
appAutofocus
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import {
|
import { animate, style, transition, trigger } from "@angular/animations";
|
||||||
animate,
|
import { Component } from "@angular/core";
|
||||||
style,
|
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||||
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({
|
@Component({
|
||||||
selector: 'app-verify-master-password',
|
selector: "app-verify-master-password",
|
||||||
templateUrl: 'verify-master-password.component.html',
|
templateUrl: "verify-master-password.component.html",
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: NG_VALUE_ACCESSOR,
|
provide: NG_VALUE_ACCESSOR,
|
||||||
@ -20,11 +15,8 @@ import { VerifyMasterPasswordComponent as BaseComponent } from 'jslib-angular/co
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
animations: [
|
animations: [
|
||||||
trigger('sent', [
|
trigger("sent", [
|
||||||
transition(':enter', [
|
transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]),
|
||||||
style({ opacity: 0 }),
|
|
||||||
animate('100ms', style({ opacity: 1 })),
|
|
||||||
]),
|
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@ -1,14 +1,42 @@
|
|||||||
<a class="account-switcher" (click)="toggle()" cdkOverlayOrigin #trigger="cdkOverlayOrigin" [hidden]="!showSwitcher">
|
<a
|
||||||
<app-avatar [data]="activeAccountEmail" size="25" [circle]="true" [fontSize]="14" [dynamic]="true" *ngIf="activeAccountEmail != null"></app-avatar>
|
class="account-switcher"
|
||||||
|
(click)="toggle()"
|
||||||
|
cdkOverlayOrigin
|
||||||
|
#trigger="cdkOverlayOrigin"
|
||||||
|
[hidden]="!showSwitcher"
|
||||||
|
>
|
||||||
|
<app-avatar
|
||||||
|
[data]="activeAccountEmail"
|
||||||
|
size="25"
|
||||||
|
[circle]="true"
|
||||||
|
[fontSize]="14"
|
||||||
|
[dynamic]="true"
|
||||||
|
*ngIf="activeAccountEmail != null"
|
||||||
|
></app-avatar>
|
||||||
<span>{{ activeAccountEmail }}</span>
|
<span>{{ activeAccountEmail }}</span>
|
||||||
<i class="fa" aria-hidden="true" [ngClass]="{'fa-chevron-down': !isOpen, 'fa-chevron-up': isOpen}"></i>
|
<i
|
||||||
|
class="fa"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-chevron-down': !isOpen, 'fa-chevron-up': isOpen }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="trigger" [cdkConnectedOverlayOpen]="isOpen" cdkConnectedOverlayMinWidth="250px">
|
<ng-template
|
||||||
|
cdkConnectedOverlay
|
||||||
|
[cdkConnectedOverlayOrigin]="trigger"
|
||||||
|
[cdkConnectedOverlayOpen]="isOpen"
|
||||||
|
cdkConnectedOverlayMinWidth="250px"
|
||||||
|
>
|
||||||
<div class="account-switcher-dropdown" [@transformPanel]="'open'">
|
<div class="account-switcher-dropdown" [@transformPanel]="'open'">
|
||||||
<div class="accounts">
|
<div class="accounts">
|
||||||
<a *ngFor="let a of accounts | keyvalue" class="account" [ngClass]="{'active': a.value.profile.authenticationStatus == 'active'}"
|
<a
|
||||||
href="#" appStopClick (click)="switch(a.key)">
|
*ngFor="let a of accounts | keyvalue"
|
||||||
|
class="account"
|
||||||
|
[ngClass]="{ active: a.value.profile.authenticationStatus == 'active' }"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
(click)="switch(a.key)"
|
||||||
|
>
|
||||||
<span class="email">{{ a.value.profile.email }}</span>
|
<span class="email">{{ a.value.profile.email }}</span>
|
||||||
<span class="server">{{ a.value.settings.environmentUrls.server }}</span>
|
<span class="server">{{ a.value.settings.environmentUrls.server }}</span>
|
||||||
<span class="status">{{ a.value.profile.authenticationStatus }}</span>
|
<span class="status">{{ a.value.profile.authenticationStatus }}</span>
|
||||||
@ -16,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="border"></div>
|
<div class="border"></div>
|
||||||
<a class="add" routerLink="/login" (click)="toggle()">
|
<a class="add" routerLink="/login" (click)="toggle()">
|
||||||
<i class="fa fa-plus" aria-hidden="true"></i> {{'addAccount' | i18n}}
|
<i class="fa fa-plus" aria-hidden="true"></i> {{ "addAccount" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -1,33 +1,36 @@
|
|||||||
import {
|
import { animate, state, style, transition, trigger } from "@angular/animations";
|
||||||
animate,
|
import { Component, OnInit } from "@angular/core";
|
||||||
state,
|
import { Router } from "@angular/router";
|
||||||
style,
|
|
||||||
transition,
|
|
||||||
trigger,
|
|
||||||
} from '@angular/animations';
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
|
||||||
import { AuthenticationStatus } from 'jslib-common/enums/authenticationStatus';
|
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
|
||||||
|
|
||||||
import { Account } from 'jslib-common/models/domain/account';
|
import { Account } from "jslib-common/models/domain/account";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-account-switcher',
|
selector: "app-account-switcher",
|
||||||
templateUrl: 'account-switcher.component.html',
|
templateUrl: "account-switcher.component.html",
|
||||||
animations: [
|
animations: [
|
||||||
trigger('transformPanel', [
|
trigger("transformPanel", [
|
||||||
state('void', style({
|
state(
|
||||||
|
"void",
|
||||||
|
style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
})),
|
})
|
||||||
transition('void => open', animate('100ms linear', style({
|
),
|
||||||
|
transition(
|
||||||
|
"void => open",
|
||||||
|
animate(
|
||||||
|
"100ms linear",
|
||||||
|
style({
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
}))),
|
})
|
||||||
transition('* => void', animate('100ms linear', style({opacity: 0}))),
|
)
|
||||||
|
),
|
||||||
|
transition("* => void", animate("100ms linear", style({ opacity: 0 }))),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -40,18 +43,24 @@ export class AccountSwitcherComponent implements OnInit {
|
|||||||
return this.accounts != null && Object.keys(this.accounts).length > 0;
|
return this.accounts != null && Object.keys(this.accounts).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private stateService: StateService, private vaultTimeoutService: VaultTimeoutService,
|
constructor(
|
||||||
private messagingService: MessagingService, private router: Router) {}
|
private stateService: StateService,
|
||||||
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
|
private messagingService: MessagingService,
|
||||||
|
private router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
this.stateService.accounts.subscribe(async accounts => {
|
this.stateService.accounts.subscribe(async (accounts) => {
|
||||||
for (const userId in accounts) {
|
for (const userId in accounts) {
|
||||||
if (userId === await this.stateService.getUserId()) {
|
if (userId === (await this.stateService.getUserId())) {
|
||||||
accounts[userId].profile.authenticationStatus = AuthenticationStatus.Active;
|
accounts[userId].profile.authenticationStatus = AuthenticationStatus.Active;
|
||||||
} else {
|
} else {
|
||||||
accounts[userId].profile.authenticationStatus = await this.vaultTimeoutService.isLocked(userId) ?
|
accounts[userId].profile.authenticationStatus = (await this.vaultTimeoutService.isLocked(
|
||||||
AuthenticationStatus.Locked :
|
userId
|
||||||
AuthenticationStatus.Unlocked;
|
))
|
||||||
|
? AuthenticationStatus.Locked
|
||||||
|
: AuthenticationStatus.Unlocked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,11 +77,11 @@ export class AccountSwitcherComponent implements OnInit {
|
|||||||
await this.stateService.setActiveUser(userId);
|
await this.stateService.setActiveUser(userId);
|
||||||
const locked = await this.vaultTimeoutService.isLocked(userId);
|
const locked = await this.vaultTimeoutService.isLocked(userId);
|
||||||
if (locked) {
|
if (locked) {
|
||||||
this.messagingService.send('locked', { userId: userId });
|
this.messagingService.send("locked", { userId: userId });
|
||||||
} else {
|
} else {
|
||||||
this.messagingService.send('unlocked');
|
this.messagingService.send("unlocked");
|
||||||
this.messagingService.send('syncVault');
|
this.messagingService.send("syncVault");
|
||||||
this.router.navigate(['vault']);
|
this.router.navigate(["vault"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-header',
|
selector: "app-header",
|
||||||
templateUrl: 'header.component.html',
|
templateUrl: "header.component.html",
|
||||||
})
|
})
|
||||||
export class HeaderComponent {
|
export class HeaderComponent {}
|
||||||
}
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nav',
|
selector: "app-nav",
|
||||||
templateUrl: 'nav.component.html',
|
templateUrl: "nav.component.html",
|
||||||
})
|
})
|
||||||
export class NavComponent {
|
export class NavComponent {
|
||||||
items: any[] = [
|
items: any[] = [
|
||||||
{
|
{
|
||||||
link: '/vault',
|
link: "/vault",
|
||||||
icon: 'fa-lock',
|
icon: "fa-lock",
|
||||||
label: this.i18nService.translate('myVault'),
|
label: this.i18nService.translate("myVault"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
link: '/send',
|
link: "/send",
|
||||||
icon: 'fa-paper-plane',
|
icon: "fa-paper-plane",
|
||||||
label: 'Send',
|
label: "Send",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
export type SearchBarState = {
|
export type SearchBarState = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@ -8,12 +8,11 @@ export type SearchBarState = {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchBarService {
|
export class SearchBarService {
|
||||||
|
|
||||||
searchText = new BehaviorSubject<string>(null);
|
searchText = new BehaviorSubject<string>(null);
|
||||||
|
|
||||||
private _state = {
|
private _state = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
placeholderText: '',
|
placeholderText: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
// tslint:disable-next-line:member-ordering
|
// tslint:disable-next-line:member-ordering
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
<div class="search" *ngIf="state.enabled">
|
<div class="search" *ngIf="state.enabled">
|
||||||
<input type="search" [placeholder]="state.placeholderText" id="search" autocomplete="off" [formControl]="searchText" appAutofocus>
|
<input
|
||||||
|
type="search"
|
||||||
|
[placeholder]="state.placeholderText"
|
||||||
|
id="search"
|
||||||
|
autocomplete="off"
|
||||||
|
[formControl]="searchText"
|
||||||
|
appAutofocus
|
||||||
|
/>
|
||||||
<i class="fa fa-search" aria-hidden="true"></i>
|
<i class="fa fa-search" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from "@angular/forms";
|
||||||
|
|
||||||
import { SearchBarService, SearchBarState } from './search-bar.service';
|
import { SearchBarService, SearchBarState } from "./search-bar.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-search',
|
selector: "app-search",
|
||||||
templateUrl: 'search.component.html',
|
templateUrl: "search.component.html",
|
||||||
})
|
})
|
||||||
export class SearchComponent {
|
export class SearchComponent {
|
||||||
|
|
||||||
state: SearchBarState;
|
state: SearchBarState;
|
||||||
searchText: FormControl = new FormControl(null);
|
searchText: FormControl = new FormControl(null);
|
||||||
|
|
||||||
constructor(private searchBarService: SearchBarService) {
|
constructor(private searchBarService: SearchBarService) {
|
||||||
this.searchBarService.state.subscribe(state => {
|
this.searchBarService.state.subscribe((state) => {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.searchText.valueChanges.subscribe(value => {
|
this.searchText.valueChanges.subscribe((value) => {
|
||||||
this.searchBarService.setSearchText(value);
|
this.searchBarService.setSearchText(value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
import { enableProdMode } from "@angular/core";
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||||
|
|
||||||
import { isDev } from 'jslib-electron/utils';
|
import { isDev } from "jslib-electron/utils";
|
||||||
|
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
require('../scss/styles.scss');
|
require("../scss/styles.scss");
|
||||||
|
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from "./app.module";
|
||||||
|
|
||||||
if (!isDev()) {
|
if (!isDev()) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
@ -15,5 +15,5 @@ if (!isDev()) {
|
|||||||
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
||||||
|
|
||||||
// Disable drag and drop to prevent malicious links from executing in the context of the app
|
// Disable drag and drop to prevent malicious links from executing in the context of the app
|
||||||
document.addEventListener('dragover', event => event.preventDefault());
|
document.addEventListener("dragover", (event) => event.preventDefault());
|
||||||
document.addEventListener('drop', event => event.preventDefault());
|
document.addEventListener("drop", (event) => event.preventDefault());
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
<div class="inner-content" *ngIf="send">
|
<div class="inner-content" *ngIf="send">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<app-callout *ngIf="disableSend">
|
<app-callout *ngIf="disableSend">
|
||||||
<span>{{'sendDisabledWarning' | i18n}}</span>
|
<span>{{ "sendDisabledWarning" | i18n }}</span>
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<app-callout type="info" *ngIf="disableHideEmail && !disableSend">
|
<app-callout type="info" *ngIf="disableHideEmail && !disableSend">
|
||||||
{{'sendOptionsPolicyInEffect' | i18n}}
|
{{ "sendOptionsPolicyInEffect" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
@ -15,161 +15,282 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="name">{{'name' | i18n}}</label>
|
<label for="name">{{ "name" | i18n }}</label>
|
||||||
<input id="name" type="text" name="Name" [(ngModel)]="send.name" appAutofocus [readOnly]="disableSend">
|
<input
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
name="Name"
|
||||||
|
[(ngModel)]="send.name"
|
||||||
|
appAutofocus
|
||||||
|
[readOnly]="disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-radio" *ngIf="!editMode">
|
<div class="box-content-row box-content-row-radio" *ngIf="!editMode">
|
||||||
<label class="radio-header">{{'whatTypeOfSend' | i18n}}</label>
|
<label class="radio-header">{{ "whatTypeOfSend" | i18n }}</label>
|
||||||
<div class="item" *ngFor="let o of typeOptions">
|
<div class="item" *ngFor="let o of typeOptions">
|
||||||
<input type="radio" class="radio" [(ngModel)]="send.type" name="Type_{{o.value}}"
|
<input
|
||||||
id="type_{{o.value}}" [value]="o.value" (change)="typeChanged(o)"
|
type="radio"
|
||||||
[checked]="send.type === o.value" [disabled]="disableSend">
|
class="radio"
|
||||||
|
[(ngModel)]="send.type"
|
||||||
|
name="Type_{{ o.value }}"
|
||||||
|
id="type_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="typeChanged(o)"
|
||||||
|
[checked]="send.type === o.value"
|
||||||
|
[disabled]="disableSend"
|
||||||
|
/>
|
||||||
<label class="unstyled" for="type_{{ o.value }}">
|
<label class="unstyled" for="type_{{ o.value }}">
|
||||||
{{ o.name }}
|
{{ o.name }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBowRow *ngIf="!editMode && send.type === sendType.File">
|
<div class="box-content-row" appBowRow *ngIf="!editMode && send.type === sendType.File">
|
||||||
<label for="file">{{'file' | i18n}}</label>
|
<label for="file">{{ "file" | i18n }}</label>
|
||||||
<input type="file" id="file" class="form-control-file" name="file" required [disabled]="disableSend">
|
<input
|
||||||
|
type="file"
|
||||||
|
id="file"
|
||||||
|
class="form-control-file"
|
||||||
|
name="file"
|
||||||
|
required
|
||||||
|
[disabled]="disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBowRow *ngIf="editMode && send.type === sendType.File">
|
<div class="box-content-row" appBowRow *ngIf="editMode && send.type === sendType.File">
|
||||||
<label for="file">{{'file' | i18n}}</label>
|
<label for="file">{{ "file" | i18n }}</label>
|
||||||
<div class="row-main">{{ send.file.fileName }} ({{ send.file.sizeName }})</div>
|
<div class="row-main">{{ send.file.fileName }} ({{ send.file.sizeName }})</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow *ngIf="send.type === sendType.Text">
|
<div class="box-content-row" appBoxRow *ngIf="send.type === sendType.Text">
|
||||||
<label for="text">{{'text' | i18n}}</label>
|
<label for="text">{{ "text" | i18n }}</label>
|
||||||
<textarea id="text" name="text" [(ngModel)]="send.text.text" rows="6" [readOnly]="disableSend"></textarea>
|
<textarea
|
||||||
|
id="text"
|
||||||
|
name="text"
|
||||||
|
[(ngModel)]="send.text.text"
|
||||||
|
rows="6"
|
||||||
|
[readOnly]="disableSend"
|
||||||
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer" *ngIf="!editMode && send.type === sendType.File">
|
<div class="box-footer" *ngIf="!editMode && send.type === sendType.File">
|
||||||
{{'sendFileDesc' | i18n}} {{'maxFileSize' | i18n}}
|
{{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer" *ngIf="send.type === sendType.Text">
|
<div class="box-footer" *ngIf="send.type === sendType.Text">
|
||||||
{{'sendTextDesc' | i18n}}
|
{{ "sendTextDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="send.type === sendType.Text">
|
<div class="box" *ngIf="send.type === sendType.Text">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="hideText">{{'textHiddenByDefault' | i18n}}</label>
|
<label for="hideText">{{ "textHiddenByDefault" | i18n }}</label>
|
||||||
<input id="hideText" name="hideText" type="checkbox" [(ngModel)]="send.text.hidden" [disabled]="disableSend">
|
<input
|
||||||
|
id="hideText"
|
||||||
|
name="hideText"
|
||||||
|
type="checkbox"
|
||||||
|
[(ngModel)]="send.text.hidden"
|
||||||
|
[disabled]="disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'options' | i18n}}
|
{{ "options" | i18n }}
|
||||||
<a class="toggle" href="#" appStopClick appBlurClick role="button" (click)="toggleOptions()">
|
<a
|
||||||
<i class="fa fa-lg" aria-hidden="true" [ngClass]="{'fa-chevron-down': !showOptions, 'fa-chevron-up': showOptions}"></i>
|
class="toggle"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
(click)="toggleOptions()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-chevron-down': !showOptions, 'fa-chevron-up': showOptions }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div [hidden]="!showOptions">
|
<div [hidden]="!showOptions">
|
||||||
<app-send-efflux-dates
|
<app-send-efflux-dates
|
||||||
[initialDeletionDate]="send.deletionDate" [initialExpirationDate]="send.expirationDate"
|
[initialDeletionDate]="send.deletionDate"
|
||||||
[editMode]="editMode" [disabled]="disableSend" (datesChanged)="setDates($event)">
|
[initialExpirationDate]="send.expirationDate"
|
||||||
|
[editMode]="editMode"
|
||||||
|
[disabled]="disableSend"
|
||||||
|
(datesChanged)="setDates($event)"
|
||||||
|
>
|
||||||
</app-send-efflux-dates>
|
</app-send-efflux-dates>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="maxAccessCount">{{'maxAccessCount' | i18n}}</label>
|
<label for="maxAccessCount">{{ "maxAccessCount" | i18n }}</label>
|
||||||
<input id="maxAccessCount" type="number" name="maxAccessCount" [(ngModel)]="send.maxAccessCount" [readOnly]="disableSend">
|
<input
|
||||||
|
id="maxAccessCount"
|
||||||
|
type="number"
|
||||||
|
name="maxAccessCount"
|
||||||
|
[(ngModel)]="send.maxAccessCount"
|
||||||
|
[readOnly]="disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer" *ngIf="!editMode">
|
<div class="box-footer" *ngIf="!editMode">
|
||||||
{{'maxAccessCountDesc' | i18n}}
|
{{ "maxAccessCountDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer" *ngIf="editMode">
|
<div class="box-footer" *ngIf="editMode">
|
||||||
<p>{{'maxAccessCountDesc' | i18n}}</p>
|
<p>{{ "maxAccessCountDesc" | i18n }}</p>
|
||||||
{{'currentAccessCount' | i18n}}: <strong>{{send.accessCount}}</strong>
|
{{ "currentAccessCount" | i18n }}: <strong>{{ send.accessCount }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="password">{{(hasPassword ? 'newPassword' : 'password') | i18n}}</label>
|
<label for="password">{{
|
||||||
<input id="password" name="password" type="{{showPassword ? 'text' : 'password'}}"
|
(hasPassword ? "newPassword" : "password") | i18n
|
||||||
[(ngModel)]="password" [readOnly]="disableSend" appInputVerbatim>
|
}}</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
[(ngModel)]="password"
|
||||||
|
[readOnly]="disableSend"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePasswordVisible()" [disabled]="disableSend">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePasswordVisible()"
|
||||||
|
[disabled]="disableSend"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'sendPasswordDesc' | i18n}}
|
{{ "sendPasswordDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'notes' | i18n}}
|
{{ "notes" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<textarea id="notes" name="notes" [(ngModel)]="send.notes" rows="6" [readOnly]="disableSend"></textarea>
|
<textarea
|
||||||
|
id="notes"
|
||||||
|
name="notes"
|
||||||
|
[(ngModel)]="send.notes"
|
||||||
|
rows="6"
|
||||||
|
[readOnly]="disableSend"
|
||||||
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'sendNotesDesc' | i18n}}
|
{{ "sendNotesDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="hideEmail">{{'hideEmail' | i18n}}</label>
|
<label for="hideEmail">{{ "hideEmail" | i18n }}</label>
|
||||||
<input id="hideEmail" type="checkbox" name="HideEmail" [(ngModel)]="send.hideEmail"
|
<input
|
||||||
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend">
|
id="hideEmail"
|
||||||
|
type="checkbox"
|
||||||
|
name="HideEmail"
|
||||||
|
[(ngModel)]="send.hideEmail"
|
||||||
|
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="disabled">{{'disableSend' | i18n}}</label>
|
<label for="disabled">{{ "disableSend" | i18n }}</label>
|
||||||
<input id="disabled" type="checkbox" name="disabled" [(ngModel)]="send.disabled" [disabled]="disableSend">
|
<input
|
||||||
|
id="disabled"
|
||||||
|
type="checkbox"
|
||||||
|
name="disabled"
|
||||||
|
[(ngModel)]="send.disabled"
|
||||||
|
[disabled]="disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'share' | i18n}}
|
{{ "share" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
||||||
<label for="link">{{'sendLinkLabel' | i18n}}</label>
|
<label for="link">{{ "sendLinkLabel" | i18n }}</label>
|
||||||
<input id="link" name="link" [ngModel]="link" readonly>
|
<input id="link" name="link" [ngModel]="link" readonly />
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="copyLink">{{'copySendLinkOnSave' | i18n}}</label>
|
<label for="copyLink">{{ "copySendLinkOnSave" | i18n }}</label>
|
||||||
<input id="copyLink" name="copyLink" [(ngModel)]="copyLink" type="checkbox" [disabled]="disableSend">
|
<input
|
||||||
|
id="copyLink"
|
||||||
|
name="copyLink"
|
||||||
|
[(ngModel)]="copyLink"
|
||||||
|
type="checkbox"
|
||||||
|
[disabled]="disableSend"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button appBlurClick type="submit" class="primary btn-submit" appA11yTitle="{{'save' | i18n}}" [disabled]="form.loading" *ngIf="!disableSend">
|
<button
|
||||||
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary btn-submit"
|
||||||
|
appA11yTitle="{{ 'save' | i18n }}"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
*ngIf="!disableSend"
|
||||||
|
>
|
||||||
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||||
<span><i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i></span>
|
<span><i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i></span>
|
||||||
</button>
|
</button>
|
||||||
<button appBlurClick type="button" (click)="cancel()" [disabled]="form.loading">
|
<button appBlurClick type="button" (click)="cancel()" [disabled]="form.loading">
|
||||||
{{'cancel' | i18n}}
|
{{ "cancel" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button appBlurClick type="button" (click)="copyLinkToClipboard(link)" appA11yTitle="{{'copySendLinkToClipboard' | i18n}}" *ngIf="editMode">
|
<button
|
||||||
|
appBlurClick
|
||||||
|
type="button"
|
||||||
|
(click)="copyLinkToClipboard(link)"
|
||||||
|
appA11yTitle="{{ 'copySendLinkToClipboard' | i18n }}"
|
||||||
|
*ngIf="editMode"
|
||||||
|
>
|
||||||
<i class="fa fa-copy fa-lg fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-copy fa-lg fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
<button
|
||||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode">
|
#deleteBtn
|
||||||
|
appBlurClick
|
||||||
|
type="button"
|
||||||
|
(click)="delete()"
|
||||||
|
class="danger"
|
||||||
|
appA11yTitle="{{ 'delete' | i18n }}"
|
||||||
|
*ngIf="editMode"
|
||||||
|
>
|
||||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading" aria-hidden="true"></i>
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!deleteBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,38 +1,52 @@
|
|||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from "@angular/common";
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
import { SendService } from "jslib-common/abstractions/send.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.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({
|
@Component({
|
||||||
selector: 'app-send-add-edit',
|
selector: "app-send-add-edit",
|
||||||
templateUrl: 'add-edit.component.html',
|
templateUrl: "add-edit.component.html",
|
||||||
})
|
})
|
||||||
export class AddEditComponent extends BaseAddEditComponent {
|
export class AddEditComponent extends BaseAddEditComponent {
|
||||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
environmentService: EnvironmentService, datePipe: DatePipe,
|
i18nService: I18nService,
|
||||||
sendService: SendService, stateService: StateService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
messagingService: MessagingService, policyService: PolicyService,
|
environmentService: EnvironmentService,
|
||||||
logService: LogService) {
|
datePipe: DatePipe,
|
||||||
super(i18nService, platformUtilsService, environmentService,
|
sendService: SendService,
|
||||||
datePipe, sendService, messagingService, policyService,
|
stateService: StateService,
|
||||||
logService, stateService);
|
messagingService: MessagingService,
|
||||||
|
policyService: PolicyService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
i18nService,
|
||||||
|
platformUtilsService,
|
||||||
|
environmentService,
|
||||||
|
datePipe,
|
||||||
|
sendService,
|
||||||
|
messagingService,
|
||||||
|
policyService,
|
||||||
|
logService,
|
||||||
|
stateService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
this.password = null;
|
this.password = null;
|
||||||
const send = await this.loadSend();
|
const send = await this.loadSend();
|
||||||
this.send = await send.decrypt();
|
this.send = await send.decrypt();
|
||||||
this.hasPassword = this.send.password != null && this.send.password.trim() !== '';
|
this.hasPassword = this.send.password != null && this.send.password.trim() !== "";
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
@ -41,7 +55,10 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||||||
|
|
||||||
async copyLinkToClipboard(link: string) {
|
async copyLinkToClipboard(link: string) {
|
||||||
super.copyLinkToClipboard(link);
|
super.copyLinkToClipboard(link);
|
||||||
this.platformUtilsService.showToast('success', null,
|
this.platformUtilsService.showToast(
|
||||||
this.i18nService.t('valueCopied', this.i18nService.t('sendLink')));
|
"success",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("valueCopied", this.i18nService.t("sendLink"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,34 +2,54 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||||
<label for="deletionDate">{{'deletionDate' | i18n}}</label>
|
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
|
||||||
<select id="deletionDate" name="DeletionDateSelect" formControlName="selectedDeletionDatePreset" required>
|
<select
|
||||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{o.name}}
|
id="deletionDate"
|
||||||
</option>
|
name="DeletionDateSelect"
|
||||||
|
formControlName="selectedDeletionDatePreset"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="help-block">{{'deletionDateDesc' | i18n}}</small>
|
<small class="help-block">{{ "deletionDateDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="selectedDeletionDatePreset.value === 0 || editMode">
|
<div class="box-content-row" *ngIf="selectedDeletionDatePreset.value === 0 || editMode">
|
||||||
<label *ngIf="editMode" for="deletionDateCustom">{{'deletionDate' | i18n}}</label>
|
<label *ngIf="editMode" for="deletionDateCustom">{{ "deletionDate" | i18n }}</label>
|
||||||
<input id="deletionDateCustom" type="datetime-local" name="deletionDate"
|
<input
|
||||||
formControlName="defaultDeletionDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM">
|
id="deletionDateCustom"
|
||||||
<small class="help-block" *ngIf="editMode">{{'deletionDateDesc' | i18n}}</small>
|
type="datetime-local"
|
||||||
|
name="deletionDate"
|
||||||
|
formControlName="defaultDeletionDateTime"
|
||||||
|
required
|
||||||
|
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||||
|
/>
|
||||||
|
<small class="help-block" *ngIf="editMode">{{ "deletionDateDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||||
<label for="expirationDate">{{'expirationDate' | i18n}}</label>
|
<label for="expirationDate">{{ "expirationDate" | i18n }}</label>
|
||||||
<select id="expirationDate" name="expirationDateSelect" formControlName="selectedExpirationDatePreset" required>
|
<select
|
||||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{o.name}}
|
id="expirationDate"
|
||||||
</option>
|
name="expirationDateSelect"
|
||||||
|
formControlName="selectedExpirationDatePreset"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="help-block">{{'expirationDateDesc' | i18n}}</small>
|
<small class="help-block">{{ "expirationDateDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0 || editMode">
|
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0 || editMode">
|
||||||
<label *ngIf="editMode" for="expirationDateCustom">{{'expirationDate' | i18n}}</label>
|
<label *ngIf="editMode" for="expirationDateCustom">{{ "expirationDate" | i18n }}</label>
|
||||||
<input id="expirationDateCustom" type="datetime-local" name="expirationDate"
|
<input
|
||||||
formControlName="defaultExpirationDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM" [readOnly]="disableSend">
|
id="expirationDateCustom"
|
||||||
<small *ngIf="editMode" class="help-block">{{'expirationDateDesc' | i18n}}</small>
|
type="datetime-local"
|
||||||
|
name="expirationDate"
|
||||||
|
formControlName="defaultExpirationDateTime"
|
||||||
|
required
|
||||||
|
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||||
|
[readOnly]="disableSend"
|
||||||
|
/>
|
||||||
|
<small *ngIf="editMode" class="help-block">{{ "expirationDateDesc" | i18n }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from "@angular/common";
|
||||||
|
|
||||||
import {
|
import { Component, OnChanges } from "@angular/core";
|
||||||
Component,
|
|
||||||
OnChanges,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { ControlContainer, NgForm } from '@angular/forms';
|
import { ControlContainer, NgForm } from "@angular/forms";
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.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({
|
@Component({
|
||||||
selector: 'app-send-efflux-dates',
|
selector: "app-send-efflux-dates",
|
||||||
templateUrl: 'efflux-dates.component.html',
|
templateUrl: "efflux-dates.component.html",
|
||||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
||||||
})
|
})
|
||||||
export class EffluxDatesComponent extends BaseEffluxDatesComponent implements OnChanges {
|
export class EffluxDatesComponent extends BaseEffluxDatesComponent implements OnChanges {
|
||||||
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
protected datePipe: DatePipe) {
|
protected i18nService: I18nService,
|
||||||
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
protected datePipe: DatePipe
|
||||||
|
) {
|
||||||
super(i18nService, platformUtilsService, datePipe);
|
super(i18nService, platformUtilsService, datePipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,10 +27,13 @@ export class EffluxDatesComponent extends BaseEffluxDatesComponent implements On
|
|||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.selectedExpirationDatePreset.setValue(0);
|
this.selectedExpirationDatePreset.setValue(0);
|
||||||
this.selectedDeletionDatePreset.setValue(0);
|
this.selectedDeletionDatePreset.setValue(0);
|
||||||
this.defaultDeletionDateTime.setValue(this.datePipe.transform(new Date(this.initialDeletionDate), 'yyyy-MM-ddTHH:mm'));
|
this.defaultDeletionDateTime.setValue(
|
||||||
|
this.datePipe.transform(new Date(this.initialDeletionDate), "yyyy-MM-ddTHH:mm")
|
||||||
|
);
|
||||||
if (this.initialExpirationDate) {
|
if (this.initialExpirationDate) {
|
||||||
this.defaultExpirationDateTime.setValue(
|
this.defaultExpirationDateTime.setValue(
|
||||||
this.datePipe.transform(new Date(this.initialExpirationDate), 'yyyy-MM-ddTHH:mm'));
|
this.datePipe.transform(new Date(this.initialExpirationDate), "yyyy-MM-ddTHH:mm")
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.defaultExpirationDateTime.setValue(null);
|
this.defaultExpirationDateTime.setValue(null);
|
||||||
}
|
}
|
||||||
|
@ -3,24 +3,26 @@
|
|||||||
<div class="mac-bar"></div>
|
<div class="mac-bar"></div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="inner-content">
|
<div class="inner-content">
|
||||||
<h2 class="sr-only">{{'filters' | i18n}}</h2>
|
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li [ngClass]="{ active: selectedAll }">
|
<li [ngClass]="{ active: selectedAll }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||||
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{'allSends' | i18n}}
|
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{ "allSends" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2>{{'types' | i18n}}</h2>
|
<h2>{{ "types" | i18n }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li [ngClass]="{ active: selectedType === sendType.Text }">
|
<li [ngClass]="{ active: selectedType === sendType.Text }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.Text)">
|
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.Text)">
|
||||||
<i class="fa fa-fw fa-file-text-o" aria-hidden="true"></i> {{'sendTypeText' | i18n}}
|
<i class="fa fa-fw fa-file-text-o" aria-hidden="true"></i> {{
|
||||||
|
"sendTypeText" | i18n
|
||||||
|
}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{ active: selectedType === sendType.File }">
|
<li [ngClass]="{ active: selectedType === sendType.File }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.File)">
|
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.File)">
|
||||||
<i class="fa fa-fw fa-file-o" aria-hidden="true"></i> {{'sendTypeFile' | i18n}}
|
<i class="fa fa-fw fa-file-o" aria-hidden="true"></i> {{ "sendTypeFile" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -33,9 +35,15 @@
|
|||||||
<div id="items" class="items">
|
<div id="items" class="items">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list" *ngIf="filteredSends.length">
|
<div class="list" *ngIf="filteredSends.length">
|
||||||
<a *ngFor="let s of filteredSends" appStopClick (click)="selectSend(s.id)"
|
<a
|
||||||
title="{{'viewItem' | i18n}}" (contextmenu)="viewSendMenu(s)"
|
*ngFor="let s of filteredSends"
|
||||||
[ngClass]="{'active': s.id === sendId}" class="flex-list-item">
|
appStopClick
|
||||||
|
(click)="selectSend(s.id)"
|
||||||
|
title="{{ 'viewItem' | i18n }}"
|
||||||
|
(contextmenu)="viewSendMenu(s)"
|
||||||
|
[ngClass]="{ active: s.id === sendId }"
|
||||||
|
class="flex-list-item"
|
||||||
|
>
|
||||||
<div class="item-icon" aria-hidden="true">
|
<div class="item-icon" aria-hidden="true">
|
||||||
<i class="fa fa-fw fa-lg" [ngClass]="s.type == 0 ? 'fa-file-text-o' : 'fa-file-o'"></i>
|
<i class="fa fa-fw fa-lg" [ngClass]="s.type == 0 ? 'fa-file-text-o' : 'fa-file-o'"></i>
|
||||||
</div>
|
</div>
|
||||||
@ -44,26 +52,49 @@
|
|||||||
{{ s.name }}
|
{{ s.name }}
|
||||||
<span class="title-badges">
|
<span class="title-badges">
|
||||||
<ng-container *ngIf="s.disabled">
|
<ng-container *ngIf="s.disabled">
|
||||||
<i class="fa fa-warning" appStopProp title="{{'disabled' | i18n}}" aria-hidden="true"></i>
|
<i
|
||||||
<span class="sr-only">{{'disabled' | i18n}}</span>
|
class="fa fa-warning"
|
||||||
|
appStopProp
|
||||||
|
title="{{ 'disabled' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "disabled" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="s.password">
|
<ng-container *ngIf="s.password">
|
||||||
<i class="fa fa-key" appStopProp title="{{'password' | i18n}}" aria-hidden="true"></i>
|
<i
|
||||||
<span class="sr-only">{{'password' | i18n}}</span>
|
class="fa fa-key"
|
||||||
|
appStopProp
|
||||||
|
title="{{ 'password' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "password" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="s.maxAccessCountReached">
|
<ng-container *ngIf="s.maxAccessCountReached">
|
||||||
<i class="fa fa-ban" appStopProp title="{{'maxAccessCountReached' | i18n}}"
|
<i
|
||||||
aria-hidden="true"></i>
|
class="fa fa-ban"
|
||||||
<span class="sr-only">{{'maxAccessCountReached' | i18n}}</span>
|
appStopProp
|
||||||
|
title="{{ 'maxAccessCountReached' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "maxAccessCountReached" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="s.expired">
|
<ng-container *ngIf="s.expired">
|
||||||
<i class="fa fa-clock-o" appStopProp title="{{'expired' | i18n}}" aria-hidden="true"></i>
|
<i
|
||||||
<span class="sr-only">{{'expired' | i18n}}</span>
|
class="fa fa-clock-o"
|
||||||
|
appStopProp
|
||||||
|
title="{{ 'expired' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "expired" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="s.pendingDelete">
|
<ng-container *ngIf="s.pendingDelete">
|
||||||
<i class="fa fa-trash" appStopProp title="{{'pendingDeletion' | i18n}}"
|
<i
|
||||||
aria-hidden="true"></i>
|
class="fa fa-trash"
|
||||||
<span class="sr-only">{{'pendingDeletion' | i18n}}</span>
|
appStopProp
|
||||||
|
title="{{ 'pendingDeletion' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -75,18 +106,31 @@
|
|||||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||||
<ng-container *ngIf="loaded">
|
<ng-container *ngIf="loaded">
|
||||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||||
<p>{{'noItemsInList' | i18n}}</p>
|
<p>{{ "noItemsInList" | i18n }}</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button appBlurClick (click)="addSend()" class="block primary" appA11yTitle="{{'addItem' | i18n}}">
|
<button
|
||||||
|
appBlurClick
|
||||||
|
(click)="addSend()"
|
||||||
|
class="block primary"
|
||||||
|
appA11yTitle="{{ 'addItem' | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-send-add-edit id="addEdit" class="details" *ngIf="action == 'add' || action == 'edit'" [sendId]="sendId" [type]="selectedSendType"
|
<app-send-add-edit
|
||||||
(onSavedSend)="savedSend($event)" (onCancelled)="cancel($event)" (onDeletedSend)="deletedSend($event)"></app-send-add-edit>
|
id="addEdit"
|
||||||
|
class="details"
|
||||||
|
*ngIf="action == 'add' || action == 'edit'"
|
||||||
|
[sendId]="sendId"
|
||||||
|
[type]="selectedSendType"
|
||||||
|
(onSavedSend)="savedSend($event)"
|
||||||
|
(onCancelled)="cancel($event)"
|
||||||
|
(onDeletedSend)="deletedSend($event)"
|
||||||
|
></app-send-add-edit>
|
||||||
<div class="logo" *ngIf="!action">
|
<div class="logo" *ngIf="!action">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="inner-content">
|
<div class="inner-content">
|
||||||
|
@ -1,40 +1,34 @@
|
|||||||
import {
|
import { Component, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||||
Component,
|
|
||||||
NgZone,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
ViewChild,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
import { SendService } from "jslib-common/abstractions/send.service";
|
||||||
|
|
||||||
import { SendComponent as BaseSendComponent } from 'jslib-angular/components/send/send.component';
|
import { SendComponent as BaseSendComponent } from "jslib-angular/components/send/send.component";
|
||||||
|
|
||||||
import { invokeMenu, RendererMenuItem } from 'jslib-electron/utils';
|
import { invokeMenu, RendererMenuItem } from "jslib-electron/utils";
|
||||||
|
|
||||||
import { SendView } from 'jslib-common/models/view/sendView';
|
import { SendView } from "jslib-common/models/view/sendView";
|
||||||
|
|
||||||
import { SearchBarService } from '../layout/search/search-bar.service';
|
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||||
import { AddEditComponent } from './add-edit.component';
|
import { AddEditComponent } from "./add-edit.component";
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
None = '',
|
None = "",
|
||||||
Add = 'add',
|
Add = "add",
|
||||||
Edit = 'edit',
|
Edit = "edit",
|
||||||
}
|
}
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'SendComponent';
|
const BroadcasterSubscriptionId = "SendComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-send',
|
selector: "app-send",
|
||||||
templateUrl: 'send.component.html',
|
templateUrl: "send.component.html",
|
||||||
})
|
})
|
||||||
export class SendComponent extends BaseSendComponent implements OnInit, OnDestroy {
|
export class SendComponent extends BaseSendComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
|
@ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
|
||||||
@ -42,15 +36,29 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
|
|||||||
sendId: string;
|
sendId: string;
|
||||||
action: Action = Action.None;
|
action: Action = Action.None;
|
||||||
|
|
||||||
constructor(sendService: SendService, i18nService: I18nService,
|
constructor(
|
||||||
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
|
sendService: SendService,
|
||||||
private broadcasterService: BroadcasterService, ngZone: NgZone,
|
i18nService: I18nService,
|
||||||
searchService: SearchService, policyService: PolicyService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
private searchBarService: SearchBarService, logService: LogService) {
|
environmentService: EnvironmentService,
|
||||||
super(sendService, i18nService, platformUtilsService,
|
private broadcasterService: BroadcasterService,
|
||||||
environmentService, ngZone, searchService,
|
ngZone: NgZone,
|
||||||
policyService, logService);
|
searchService: SearchService,
|
||||||
this.searchBarService.searchText.subscribe(searchText => {
|
policyService: PolicyService,
|
||||||
|
private searchBarService: SearchBarService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
sendService,
|
||||||
|
i18nService,
|
||||||
|
platformUtilsService,
|
||||||
|
environmentService,
|
||||||
|
ngZone,
|
||||||
|
searchService,
|
||||||
|
policyService,
|
||||||
|
logService
|
||||||
|
);
|
||||||
|
this.searchBarService.searchText.subscribe((searchText) => {
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
this.searchTextChanged();
|
this.searchTextChanged();
|
||||||
});
|
});
|
||||||
@ -58,13 +66,13 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.searchBarService.setEnabled(true);
|
this.searchBarService.setEnabled(true);
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchSends'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchSends"));
|
||||||
|
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||||
this.ngZone.run(async () => {
|
this.ngZone.run(async () => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'syncCompleted':
|
case "syncCompleted":
|
||||||
await this.load();
|
await this.load();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -116,17 +124,17 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
|
|||||||
}
|
}
|
||||||
|
|
||||||
get selectedSendType() {
|
get selectedSendType() {
|
||||||
return this.sends.find(s => s.id === this.sendId)?.type;
|
return this.sends.find((s) => s.id === this.sendId)?.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewSendMenu(send: SendView) {
|
viewSendMenu(send: SendView) {
|
||||||
const menu: RendererMenuItem[] = [];
|
const menu: RendererMenuItem[] = [];
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('copyLink'),
|
label: this.i18nService.t("copyLink"),
|
||||||
click: () => this.copy(send),
|
click: () => this.copy(send),
|
||||||
});
|
});
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('delete'),
|
label: this.i18nService.t("delete"),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
await this.delete(send);
|
await this.delete(send);
|
||||||
await this.deletedSend(send);
|
await this.deletedSend(send);
|
||||||
|
@ -1,57 +1,60 @@
|
|||||||
import {
|
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||||
APP_INITIALIZER,
|
|
||||||
NgModule,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { ElectronLogService } from 'jslib-electron/services/electronLog.service';
|
import { ElectronLogService } from "jslib-electron/services/electronLog.service";
|
||||||
import { ElectronPlatformUtilsService } from 'jslib-electron/services/electronPlatformUtils.service';
|
import { ElectronPlatformUtilsService } from "jslib-electron/services/electronPlatformUtils.service";
|
||||||
import { ElectronRendererMessagingService } from 'jslib-electron/services/electronRendererMessaging.service';
|
import { ElectronRendererMessagingService } from "jslib-electron/services/electronRendererMessaging.service";
|
||||||
import { ElectronRendererSecureStorageService } from 'jslib-electron/services/electronRendererSecureStorage.service';
|
import { ElectronRendererSecureStorageService } from "jslib-electron/services/electronRendererSecureStorage.service";
|
||||||
import { ElectronRendererStorageService } from 'jslib-electron/services/electronRendererStorage.service';
|
import { ElectronRendererStorageService } from "jslib-electron/services/electronRendererStorage.service";
|
||||||
|
|
||||||
import { I18nService } from '../services/i18n.service';
|
import { I18nService } from "../services/i18n.service";
|
||||||
import { LoginGuardService } from '../services/loginGuard.service';
|
import { LoginGuardService } from "../services/loginGuard.service";
|
||||||
import { NativeMessagingService } from '../services/nativeMessaging.service';
|
import { NativeMessagingService } from "../services/nativeMessaging.service";
|
||||||
import { PasswordRepromptService } from '../services/passwordReprompt.service';
|
import { PasswordRepromptService } from "../services/passwordReprompt.service";
|
||||||
import { SearchBarService } from './layout/search/search-bar.service';
|
import { SearchBarService } from "./layout/search/search-bar.service";
|
||||||
|
|
||||||
import { JslibServicesModule } from 'jslib-angular/services/jslib-services.module';
|
import { JslibServicesModule } from "jslib-angular/services/jslib-services.module";
|
||||||
|
|
||||||
import { AuthService } from 'jslib-common/services/auth.service';
|
import { AuthService } from "jslib-common/services/auth.service";
|
||||||
import { ContainerService } from 'jslib-common/services/container.service';
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
import { EventService } from 'jslib-common/services/event.service';
|
import { EventService } from "jslib-common/services/event.service";
|
||||||
import { SystemService } from 'jslib-common/services/system.service';
|
import { SystemService } from "jslib-common/services/system.service";
|
||||||
import { VaultTimeoutService } from 'jslib-common/services/vaultTimeout.service';
|
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||||
|
|
||||||
import { ElectronCryptoService } from 'jslib-electron/services/electronCrypto.service';
|
import { ElectronCryptoService } from "jslib-electron/services/electronCrypto.service";
|
||||||
|
|
||||||
import { AuthService as AuthServiceAbstraction } from 'jslib-common/abstractions/auth.service';
|
import { AuthService as AuthServiceAbstraction } from "jslib-common/abstractions/auth.service";
|
||||||
import { BroadcasterService as BroadcasterServiceAbstraction } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CryptoService as CryptoServiceAbstraction } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib-common/abstractions/cryptoFunction.service';
|
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service";
|
||||||
import { EventService as EventServiceAbstraction } from 'jslib-common/abstractions/event.service';
|
import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||||
import { I18nService as I18nServiceAbstraction } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService as LogServiceAbstraction } from 'jslib-common/abstractions/log.service';
|
import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService as MessagingServiceAbstraction } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||||
import { NotificationsService as NotificationsServiceAbstraction } from 'jslib-common/abstractions/notifications.service';
|
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from 'jslib-common/abstractions/passwordReprompt.service';
|
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService as StateServiceAbstraction } from 'jslib-common/abstractions/state.service';
|
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||||
import { StorageService as StorageServiceAbstraction } from 'jslib-common/abstractions/storage.service';
|
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||||
import { SyncService as SyncServiceAbstraction } from 'jslib-common/abstractions/sync.service';
|
import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service";
|
||||||
import { SystemService as SystemServiceAbstraction } from 'jslib-common/abstractions/system.service';
|
import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service";
|
||||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib-common/abstractions/vaultTimeout.service';
|
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
|
|
||||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
import { ThemeType } from "jslib-common/enums/themeType";
|
||||||
|
|
||||||
export function initFactory(window: Window, environmentService: EnvironmentServiceAbstraction,
|
|
||||||
syncService: SyncServiceAbstraction, vaultTimeoutService: VaultTimeoutService,
|
|
||||||
i18nService: I18nService, eventService: EventService,
|
|
||||||
authService: AuthService, notificationsService: NotificationsServiceAbstraction,
|
|
||||||
platformUtilsService: PlatformUtilsServiceAbstraction, stateService: StateServiceAbstraction,
|
|
||||||
cryptoService: CryptoServiceAbstraction): Function {
|
|
||||||
|
|
||||||
|
export function initFactory(
|
||||||
|
window: Window,
|
||||||
|
environmentService: EnvironmentServiceAbstraction,
|
||||||
|
syncService: SyncServiceAbstraction,
|
||||||
|
vaultTimeoutService: VaultTimeoutService,
|
||||||
|
i18nService: I18nService,
|
||||||
|
eventService: EventService,
|
||||||
|
authService: AuthService,
|
||||||
|
notificationsService: NotificationsServiceAbstraction,
|
||||||
|
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||||
|
stateService: StateServiceAbstraction,
|
||||||
|
cryptoService: CryptoServiceAbstraction
|
||||||
|
): Function {
|
||||||
return async () => {
|
return async () => {
|
||||||
await stateService.init();
|
await stateService.init();
|
||||||
await environmentService.setUrlsFromStorage();
|
await environmentService.setUrlsFromStorage();
|
||||||
@ -63,15 +66,15 @@ export function initFactory(window: Window, environmentService: EnvironmentServi
|
|||||||
authService.init();
|
authService.init();
|
||||||
setTimeout(() => notificationsService.init(), 3000);
|
setTimeout(() => notificationsService.init(), 3000);
|
||||||
const htmlEl = window.document.documentElement;
|
const htmlEl = window.document.documentElement;
|
||||||
htmlEl.classList.add('os_' + platformUtilsService.getDeviceString());
|
htmlEl.classList.add("os_" + platformUtilsService.getDeviceString());
|
||||||
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
htmlEl.classList.add("locale_" + i18nService.translationLocale);
|
||||||
const theme = await platformUtilsService.getEffectiveTheme();
|
const theme = await platformUtilsService.getEffectiveTheme();
|
||||||
htmlEl.classList.add('theme_' + theme);
|
htmlEl.classList.add("theme_" + theme);
|
||||||
platformUtilsService.onDefaultSystemThemeChange(async sysTheme => {
|
platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||||
const bwTheme = await stateService.getTheme();
|
const bwTheme = await stateService.getTheme();
|
||||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||||
htmlEl.classList.remove('theme_' + ThemeType.Light, 'theme_' + ThemeType.Dark);
|
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||||
htmlEl.classList.add('theme_' + sysTheme);
|
htmlEl.classList.add("theme_" + sysTheme);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,9 +82,9 @@ export function initFactory(window: Window, environmentService: EnvironmentServi
|
|||||||
const installedVersion = await stateService.getInstalledVersion();
|
const installedVersion = await stateService.getInstalledVersion();
|
||||||
const currentVersion = await platformUtilsService.getApplicationVersion();
|
const currentVersion = await platformUtilsService.getApplicationVersion();
|
||||||
if (installedVersion == null) {
|
if (installedVersion == null) {
|
||||||
installAction = 'install';
|
installAction = "install";
|
||||||
} else if (installedVersion !== currentVersion) {
|
} else if (installedVersion !== currentVersion) {
|
||||||
installAction = 'update';
|
installAction = "update";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (installAction != null) {
|
if (installAction != null) {
|
||||||
@ -94,16 +97,14 @@ export function initFactory(window: Window, environmentService: EnvironmentServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [JslibServicesModule],
|
||||||
JslibServicesModule,
|
|
||||||
],
|
|
||||||
declarations: [],
|
declarations: [],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initFactory,
|
useFactory: initFactory,
|
||||||
deps: [
|
deps: [
|
||||||
'WINDOW',
|
"WINDOW",
|
||||||
EnvironmentServiceAbstraction,
|
EnvironmentServiceAbstraction,
|
||||||
SyncServiceAbstraction,
|
SyncServiceAbstraction,
|
||||||
VaultTimeoutServiceAbstraction,
|
VaultTimeoutServiceAbstraction,
|
||||||
@ -120,19 +121,17 @@ export function initFactory(window: Window, environmentService: EnvironmentServi
|
|||||||
{ provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] },
|
{ provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] },
|
||||||
{
|
{
|
||||||
provide: PlatformUtilsServiceAbstraction,
|
provide: PlatformUtilsServiceAbstraction,
|
||||||
useFactory: (i18nService: I18nServiceAbstraction, messagingService: MessagingServiceAbstraction,
|
useFactory: (
|
||||||
stateService: StateServiceAbstraction) => new ElectronPlatformUtilsService(i18nService,
|
i18nService: I18nServiceAbstraction,
|
||||||
messagingService, true, stateService),
|
messagingService: MessagingServiceAbstraction,
|
||||||
deps: [
|
stateService: StateServiceAbstraction
|
||||||
I18nServiceAbstraction,
|
) => new ElectronPlatformUtilsService(i18nService, messagingService, true, stateService),
|
||||||
MessagingServiceAbstraction,
|
deps: [I18nServiceAbstraction, MessagingServiceAbstraction, StateServiceAbstraction],
|
||||||
StateServiceAbstraction,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: I18nServiceAbstraction,
|
provide: I18nServiceAbstraction,
|
||||||
useFactory: (window: Window) => new I18nService(window.navigator.language, './locales'),
|
useFactory: (window: Window) => new I18nService(window.navigator.language, "./locales"),
|
||||||
deps: [ 'WINDOW' ],
|
deps: ["WINDOW"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: MessagingServiceAbstraction,
|
provide: MessagingServiceAbstraction,
|
||||||
@ -140,7 +139,7 @@ export function initFactory(window: Window, environmentService: EnvironmentServi
|
|||||||
deps: [BroadcasterServiceAbstraction],
|
deps: [BroadcasterServiceAbstraction],
|
||||||
},
|
},
|
||||||
{ provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService },
|
{ provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService },
|
||||||
{ provide: 'SECURE_STORAGE', useClass: ElectronRendererSecureStorageService },
|
{ provide: "SECURE_STORAGE", useClass: ElectronRendererSecureStorageService },
|
||||||
{
|
{
|
||||||
provide: CryptoServiceAbstraction,
|
provide: CryptoServiceAbstraction,
|
||||||
useClass: ElectronCryptoService,
|
useClass: ElectronCryptoService,
|
||||||
@ -167,13 +166,8 @@ export function initFactory(window: Window, environmentService: EnvironmentServi
|
|||||||
{
|
{
|
||||||
provide: LoginGuardService,
|
provide: LoginGuardService,
|
||||||
useClass: LoginGuardService,
|
useClass: LoginGuardService,
|
||||||
deps: [
|
deps: [StateServiceAbstraction, PlatformUtilsServiceAbstraction, I18nServiceAbstraction],
|
||||||
StateServiceAbstraction,
|
|
||||||
PlatformUtilsServiceAbstraction,
|
|
||||||
I18nServiceAbstraction,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ServicesModule {
|
export class ServicesModule {}
|
||||||
}
|
|
||||||
|
@ -1,45 +1,97 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'customFields' | i18n}}
|
{{ "customFields" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||||
<div class="box-content-row box-content-row-multi box-draggable-row" cdkDrag
|
<div
|
||||||
|
class="box-content-row box-content-row-multi box-draggable-row"
|
||||||
|
cdkDrag
|
||||||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||||
[ngClass]="{'box-content-row-checkbox': f.type === fieldType.Boolean}">
|
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
||||||
<a href="#" appStopClick (click)="removeField(f)" appA11yTitle="{{'remove' | i18n}}"
|
>
|
||||||
role="button">
|
<a
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
(click)="removeField(f)"
|
||||||
|
appA11yTitle="{{ 'remove' | i18n }}"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
<label for="fieldName{{i}}" class="sr-only">{{'name' | i18n}}</label>
|
<label for="fieldName{{ i }}" class="sr-only">{{ "name" | i18n }}</label>
|
||||||
<label for="fieldValue{{i}}" class="sr-only">{{'value' | i18n}}</label>
|
<label for="fieldValue{{ i }}" class="sr-only">{{ "value" | i18n }}</label>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name"
|
<input
|
||||||
class="row-label" placeholder="{{'name' | i18n}}" appInputVerbatim>
|
id="fieldName{{ i }}"
|
||||||
|
type="text"
|
||||||
|
name="Field.Name{{ i }}"
|
||||||
|
[(ngModel)]="f.name"
|
||||||
|
class="row-label"
|
||||||
|
placeholder="{{ 'name' | i18n }}"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
<!-- Text -->
|
<!-- Text -->
|
||||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
<input
|
||||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}" appInputVerbatim>
|
id="fieldValue{{ i }}"
|
||||||
|
type="text"
|
||||||
|
name="Field.Value{{ i }}"
|
||||||
|
[(ngModel)]="f.value"
|
||||||
|
*ngIf="f.type === fieldType.Text"
|
||||||
|
placeholder="{{ 'value' | i18n }}"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
<!-- Password -->
|
<!-- Password -->
|
||||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
<input
|
||||||
name="Field.Value{{i}}" [(ngModel)]="f.value" class="monospaced"
|
id="fieldValue{{ i }}"
|
||||||
*ngIf="f.type === fieldType.Hidden" placeholder="{{'value' | i18n}}"
|
type="{{ f.showValue ? 'text' : 'password' }}"
|
||||||
[disabled]="!cipher.viewPassword && !f.newField" appInputVerbatim>
|
name="Field.Value{{ i }}"
|
||||||
|
[(ngModel)]="f.value"
|
||||||
|
class="monospaced"
|
||||||
|
*ngIf="f.type === fieldType.Hidden"
|
||||||
|
placeholder="{{ 'value' | i18n }}"
|
||||||
|
[disabled]="!cipher.viewPassword && !f.newField"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
<!-- Linked -->
|
<!-- Linked -->
|
||||||
<select id="fieldValue{{i}}" name="Field.Value{{i}}" [(ngModel)]="f.linkedId"
|
<select
|
||||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null">
|
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>
|
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!-- Boolean -->
|
<!-- Boolean -->
|
||||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox" [(ngModel)]="f.value"
|
<input
|
||||||
*ngIf="f.type === fieldType.Boolean" appTrueFalseValue trueValue="true"
|
id="fieldValue{{ i }}"
|
||||||
falseValue="false">
|
name="Field.Value{{ i }}"
|
||||||
<div class="action-buttons"
|
type="checkbox"
|
||||||
*ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)">
|
[(ngModel)]="f.value"
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
*ngIf="f.type === fieldType.Boolean"
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleFieldValue(f)">
|
appTrueFalseValue
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
trueValue="true"
|
||||||
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}"></i>
|
falseValue="false"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="action-buttons"
|
||||||
|
*ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="toggleFieldValue(f)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="drag-handle" appA11yTitle="{{ 'dragToSort' | i18n }}" cdkDragHandle>
|
<div class="drag-handle" appA11yTitle="{{ 'dragToSort' | i18n }}" cdkDragHandle>
|
||||||
@ -50,12 +102,16 @@
|
|||||||
<!-- Add new custom field -->
|
<!-- Add new custom field -->
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<a href="#" appStopClick (click)="addField()" role="button">
|
<a href="#" appStopClick (click)="addField()" role="button">
|
||||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{'newCustomField' | i18n}}
|
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i>
|
||||||
|
{{ "newCustomField" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
<label for="addFieldType" class="sr-only">{{ "type" | i18n }}</label>
|
||||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
<option *ngIf="cipher.linkedFieldOptions != null" [ngValue]="addFieldLinkedTypeOption.value">
|
<option
|
||||||
|
*ngIf="cipher.linkedFieldOptions != null"
|
||||||
|
[ngValue]="addFieldLinkedTypeOption.value"
|
||||||
|
>
|
||||||
{{ addFieldLinkedTypeOption.name }}
|
{{ addFieldLinkedTypeOption.name }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import {
|
import { AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent } from "jslib-angular/components/add-edit-custom-fields.component";
|
||||||
AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent
|
|
||||||
} from 'jslib-angular/components/add-edit-custom-fields.component';
|
|
||||||
|
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-add-edit-custom-fields',
|
selector: "app-vault-add-edit-custom-fields",
|
||||||
templateUrl: 'add-edit-custom-fields.component.html',
|
templateUrl: "add-edit-custom-fields.component.html",
|
||||||
})
|
})
|
||||||
export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsComponent {
|
export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsComponent {
|
||||||
constructor(i18nService: I18nService, eventService: EventService) {
|
constructor(i18nService: I18nService, eventService: EventService) {
|
||||||
|
@ -3,113 +3,208 @@
|
|||||||
<div class="inner-content" *ngIf="cipher">
|
<div class="inner-content" *ngIf="cipher">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<app-callout type="info" *ngIf="allowOwnershipOptions() && !allowPersonal">
|
<app-callout type="info" *ngIf="allowOwnershipOptions() && !allowPersonal">
|
||||||
{{'personalOwnershipPolicyInEffect' | i18n}}
|
{{ "personalOwnershipPolicyInEffect" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||||
<label for="type">{{'type' | i18n}}</label>
|
<label for="type">{{ "type" | i18n }}</label>
|
||||||
<select id="type" name="Type" [(ngModel)]="cipher.type">
|
<select id="type" name="Type" [(ngModel)]="cipher.type">
|
||||||
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="name">{{'name' | i18n}}</label>
|
<label for="name">{{ "name" | i18n }}</label>
|
||||||
<input id="name" type="text" name="Name" [(ngModel)]="cipher.name" [appAutofocus]="!editMode">
|
<input
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
name="Name"
|
||||||
|
[(ngModel)]="cipher.name"
|
||||||
|
[appAutofocus]="!editMode"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Login -->
|
<!-- Login -->
|
||||||
<div *ngIf="cipher.type === cipherType.Login">
|
<div *ngIf="cipher.type === cipherType.Login">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="loginUsername">{{'username' | i18n}}</label>
|
<label for="loginUsername">{{ "username" | i18n }}</label>
|
||||||
<input id="loginUsername" type="text" name="Login.Username"
|
<input
|
||||||
[(ngModel)]="cipher.login.username" appInputVerbatim>
|
id="loginUsername"
|
||||||
|
type="text"
|
||||||
|
name="Login.Username"
|
||||||
|
[(ngModel)]="cipher.login.username"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="loginPassword">{{'password' | i18n}}</label>
|
<label for="loginPassword">{{ "password" | i18n }}</label>
|
||||||
<input id="loginPassword" class="monospaced"
|
<input
|
||||||
type="{{showPassword ? 'text' : 'password'}}" name="Login.Password"
|
id="loginPassword"
|
||||||
[(ngModel)]="cipher.login.password" [disabled]="!cipher.viewPassword"
|
class="monospaced"
|
||||||
appInputVerbatim>
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
name="Login.Password"
|
||||||
|
[(ngModel)]="cipher.login.password"
|
||||||
|
[disabled]="!cipher.viewPassword"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons" *ngIf=cipher.viewPassword>
|
<div class="action-buttons" *ngIf="cipher.viewPassword">
|
||||||
<button type="button" #checkPasswordBtn class="row-btn btn" appBlurClick
|
<button
|
||||||
appA11yTitle="{{'checkPassword' | i18n}}" (click)="checkPassword()"
|
type="button"
|
||||||
[appApiAction]="checkPasswordPromise" [disabled]="checkPasswordBtn.loading">
|
#checkPasswordBtn
|
||||||
<i class="fa fa-lg fa-check-circle" [hidden]="checkPasswordBtn.loading"
|
class="row-btn btn"
|
||||||
aria-hidden="true"></i>
|
appBlurClick
|
||||||
<i class="fa fa-lg fa-spinner fa-spin" [hidden]="!checkPasswordBtn.loading"
|
appA11yTitle="{{ 'checkPassword' | i18n }}"
|
||||||
aria-hidden="true"></i>
|
(click)="checkPassword()"
|
||||||
|
[appApiAction]="checkPasswordPromise"
|
||||||
|
[disabled]="checkPasswordBtn.loading"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg fa-check-circle"
|
||||||
|
[hidden]="checkPasswordBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg fa-spinner fa-spin"
|
||||||
|
[hidden]="!checkPasswordBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'generatePassword' | i18n}}" (click)="generatePassword()">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'generatePassword' | i18n }}"
|
||||||
|
(click)="generatePassword()"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-refresh" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-refresh" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
<label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label>
|
||||||
<input id="loginTotp" type="{{cipher.viewPassword ? 'text' : 'password'}}" name="Login.Totp"
|
<input
|
||||||
class="monospaced" [(ngModel)]="cipher.login.totp" [disabled]="!cipher.viewPassword"
|
id="loginTotp"
|
||||||
appInputVerbatim>
|
type="{{ cipher.viewPassword ? 'text' : 'password' }}"
|
||||||
|
name="Login.Totp"
|
||||||
|
class="monospaced"
|
||||||
|
[(ngModel)]="cipher.login.totp"
|
||||||
|
[disabled]="!cipher.viewPassword"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Card -->
|
<!-- Card -->
|
||||||
<div *ngIf="cipher.type === cipherType.Card">
|
<div *ngIf="cipher.type === cipherType.Card">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="cardCardholderName">{{'cardholderName' | i18n}}</label>
|
<label for="cardCardholderName">{{ "cardholderName" | i18n }}</label>
|
||||||
<input id="cardCardholderName" type="text" name="Card.CardCardholderName"
|
<input
|
||||||
[(ngModel)]="cipher.card.cardholderName">
|
id="cardCardholderName"
|
||||||
|
type="text"
|
||||||
|
name="Card.CardCardholderName"
|
||||||
|
[(ngModel)]="cipher.card.cardholderName"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="cardNumber">{{'number' | i18n}}</label>
|
<label for="cardNumber">{{ "number" | i18n }}</label>
|
||||||
<input id="cardNumber" class="monospaced" type="{{showCardNumber ? 'text' : 'password'}}"
|
<input
|
||||||
name="Card.Number" [(ngModel)]="cipher.card.number" appInputVerbatim>
|
id="cardNumber"
|
||||||
|
class="monospaced"
|
||||||
|
type="{{ showCardNumber ? 'text' : 'password' }}"
|
||||||
|
name="Card.Number"
|
||||||
|
[(ngModel)]="cipher.card.number"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleCardNumber()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="toggleCardNumber()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="cardBrand">{{'brand' | i18n}}</label>
|
<label for="cardBrand">{{ "brand" | i18n }}</label>
|
||||||
<select id="cardBrand" name="Card.Brand" [(ngModel)]="cipher.card.brand">
|
<select id="cardBrand" name="Card.Brand" [(ngModel)]="cipher.card.brand">
|
||||||
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="cardExpMonth">{{'expirationMonth' | i18n}}</label>
|
<label for="cardExpMonth">{{ "expirationMonth" | i18n }}</label>
|
||||||
<select id="cardExpMonth" name="Card.ExpMonth" [(ngModel)]="cipher.card.expMonth">
|
<select id="cardExpMonth" name="Card.ExpMonth" [(ngModel)]="cipher.card.expMonth">
|
||||||
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">{{o.name}}</option>
|
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">
|
||||||
|
{{ o.name }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="cardExpYear">{{'expirationYear' | i18n}}</label>
|
<label for="cardExpYear">{{ "expirationYear" | i18n }}</label>
|
||||||
<input id="cardExpYear" type="text" name="Card.ExpYear" [(ngModel)]="cipher.card.expYear"
|
<input
|
||||||
placeholder="{{'ex' | i18n}} {{currentDate | date: 'yyyy'}}">
|
id="cardExpYear"
|
||||||
|
type="text"
|
||||||
|
name="Card.ExpYear"
|
||||||
|
[(ngModel)]="cipher.card.expYear"
|
||||||
|
placeholder="{{ 'ex' | i18n }} {{ currentDate | date: 'yyyy' }}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="cardCode">{{'securityCode' | i18n}}</label>
|
<label for="cardCode">{{ "securityCode" | i18n }}</label>
|
||||||
<input id="cardCode" class="monospaced" type="{{showCardCode ? 'text' : 'password'}}"
|
<input
|
||||||
name="Card.Code" [(ngModel)]="cipher.card.code" appInputVerbatim>
|
id="cardCode"
|
||||||
|
class="monospaced"
|
||||||
|
type="{{ showCardCode ? 'text' : 'password' }}"
|
||||||
|
name="Card.Code"
|
||||||
|
[(ngModel)]="cipher.card.code"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleCardCode()">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode}"></i>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="toggleCardCode()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -117,92 +212,170 @@
|
|||||||
<!-- Identity -->
|
<!-- Identity -->
|
||||||
<div *ngIf="cipher.type === cipherType.Identity">
|
<div *ngIf="cipher.type === cipherType.Identity">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idTitle">{{'title' | i18n}}</label>
|
<label for="idTitle">{{ "title" | i18n }}</label>
|
||||||
<select id="idTitle" name="Identity.Title" [(ngModel)]="cipher.identity.title">
|
<select id="idTitle" name="Identity.Title" [(ngModel)]="cipher.identity.title">
|
||||||
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">{{o.name}}</option>
|
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">
|
||||||
|
{{ o.name }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idFirstName">{{'firstName' | i18n}}</label>
|
<label for="idFirstName">{{ "firstName" | i18n }}</label>
|
||||||
<input id="idFirstName" type="text" name="Identity.FirstName"
|
<input
|
||||||
[(ngModel)]="cipher.identity.firstName">
|
id="idFirstName"
|
||||||
|
type="text"
|
||||||
|
name="Identity.FirstName"
|
||||||
|
[(ngModel)]="cipher.identity.firstName"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idMiddleName">{{'middleName' | i18n}}</label>
|
<label for="idMiddleName">{{ "middleName" | i18n }}</label>
|
||||||
<input id="idMiddleName" type="text" name="Identity.MiddleName"
|
<input
|
||||||
[(ngModel)]="cipher.identity.middleName">
|
id="idMiddleName"
|
||||||
|
type="text"
|
||||||
|
name="Identity.MiddleName"
|
||||||
|
[(ngModel)]="cipher.identity.middleName"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idLastName">{{'lastName' | i18n}}</label>
|
<label for="idLastName">{{ "lastName" | i18n }}</label>
|
||||||
<input id="idLastName" type="text" name="Identity.LastName"
|
<input
|
||||||
[(ngModel)]="cipher.identity.lastName">
|
id="idLastName"
|
||||||
|
type="text"
|
||||||
|
name="Identity.LastName"
|
||||||
|
[(ngModel)]="cipher.identity.lastName"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idUsername">{{'username' | i18n}}</label>
|
<label for="idUsername">{{ "username" | i18n }}</label>
|
||||||
<input id="idUsername" type="text" name="Identity.Username"
|
<input
|
||||||
[(ngModel)]="cipher.identity.username" appInputVerbatim>
|
id="idUsername"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Username"
|
||||||
|
[(ngModel)]="cipher.identity.username"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idCompany">{{'company' | i18n}}</label>
|
<label for="idCompany">{{ "company" | i18n }}</label>
|
||||||
<input id="idCompany" type="text" name="Identity.Company"
|
<input
|
||||||
[(ngModel)]="cipher.identity.company">
|
id="idCompany"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Company"
|
||||||
|
[(ngModel)]="cipher.identity.company"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idSsn">{{'ssn' | i18n}}</label>
|
<label for="idSsn">{{ "ssn" | i18n }}</label>
|
||||||
<input id="idSsn" type="text" name="Identity.SSN" [(ngModel)]="cipher.identity.ssn"
|
<input
|
||||||
appInputVerbatim>
|
id="idSsn"
|
||||||
|
type="text"
|
||||||
|
name="Identity.SSN"
|
||||||
|
[(ngModel)]="cipher.identity.ssn"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idPassportNumber">{{'passportNumber' | i18n}}</label>
|
<label for="idPassportNumber">{{ "passportNumber" | i18n }}</label>
|
||||||
<input id="idPassportNumber" type="text" name="Identity.PassportNumber"
|
<input
|
||||||
[(ngModel)]="cipher.identity.passportNumber" appInputVerbatim>
|
id="idPassportNumber"
|
||||||
|
type="text"
|
||||||
|
name="Identity.PassportNumber"
|
||||||
|
[(ngModel)]="cipher.identity.passportNumber"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idLicenseNumber">{{'licenseNumber' | i18n}}</label>
|
<label for="idLicenseNumber">{{ "licenseNumber" | i18n }}</label>
|
||||||
<input id="idLicenseNumber" type="text" name="Identity.LicenseNumber"
|
<input
|
||||||
[(ngModel)]="cipher.identity.licenseNumber" appInputVerbatim>
|
id="idLicenseNumber"
|
||||||
|
type="text"
|
||||||
|
name="Identity.LicenseNumber"
|
||||||
|
[(ngModel)]="cipher.identity.licenseNumber"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idEmail">{{'email' | i18n}}</label>
|
<label for="idEmail">{{ "email" | i18n }}</label>
|
||||||
<input id="idEmail" type="text" name="Identity.Email" [(ngModel)]="cipher.identity.email"
|
<input
|
||||||
appInputVerbatim>
|
id="idEmail"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Email"
|
||||||
|
[(ngModel)]="cipher.identity.email"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idPhone">{{'phone' | i18n}}</label>
|
<label for="idPhone">{{ "phone" | i18n }}</label>
|
||||||
<input id="idPhone" type="text" name="Identity.Phone" [(ngModel)]="cipher.identity.phone">
|
<input
|
||||||
|
id="idPhone"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Phone"
|
||||||
|
[(ngModel)]="cipher.identity.phone"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idAddress1">{{'address1' | i18n}}</label>
|
<label for="idAddress1">{{ "address1" | i18n }}</label>
|
||||||
<input id="idAddress1" type="text" name="Identity.Address1"
|
<input
|
||||||
[(ngModel)]="cipher.identity.address1">
|
id="idAddress1"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Address1"
|
||||||
|
[(ngModel)]="cipher.identity.address1"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idAddress2">{{'address2' | i18n}}</label>
|
<label for="idAddress2">{{ "address2" | i18n }}</label>
|
||||||
<input id="idAddress2" type="text" name="Identity.Address2"
|
<input
|
||||||
[(ngModel)]="cipher.identity.address2">
|
id="idAddress2"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Address2"
|
||||||
|
[(ngModel)]="cipher.identity.address2"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idAddress3">{{'address3' | i18n}}</label>
|
<label for="idAddress3">{{ "address3" | i18n }}</label>
|
||||||
<input id="idAddress3" type="text" name="Identity.Address3"
|
<input
|
||||||
[(ngModel)]="cipher.identity.address3">
|
id="idAddress3"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Address3"
|
||||||
|
[(ngModel)]="cipher.identity.address3"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idCity">{{'cityTown' | i18n}}</label>
|
<label for="idCity">{{ "cityTown" | i18n }}</label>
|
||||||
<input id="idCity" type="text" name="Identity.City" [(ngModel)]="cipher.identity.city">
|
<input
|
||||||
|
id="idCity"
|
||||||
|
type="text"
|
||||||
|
name="Identity.City"
|
||||||
|
[(ngModel)]="cipher.identity.city"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idState">{{'stateProvince' | i18n}}</label>
|
<label for="idState">{{ "stateProvince" | i18n }}</label>
|
||||||
<input id="idState" type="text" name="Identity.State" [(ngModel)]="cipher.identity.state">
|
<input
|
||||||
|
id="idState"
|
||||||
|
type="text"
|
||||||
|
name="Identity.State"
|
||||||
|
[(ngModel)]="cipher.identity.state"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idPostalCode">{{'zipPostalCode' | i18n}}</label>
|
<label for="idPostalCode">{{ "zipPostalCode" | i18n }}</label>
|
||||||
<input id="idPostalCode" type="text" name="Identity.PostalCode"
|
<input
|
||||||
[(ngModel)]="cipher.identity.postalCode">
|
id="idPostalCode"
|
||||||
|
type="text"
|
||||||
|
name="Identity.PostalCode"
|
||||||
|
[(ngModel)]="cipher.identity.postalCode"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="idCountry">{{'country' | i18n}}</label>
|
<label for="idCountry">{{ "country" | i18n }}</label>
|
||||||
<input id="idCountry" type="text" name="Identity.Country"
|
<input
|
||||||
[(ngModel)]="cipher.identity.country">
|
id="idCountry"
|
||||||
|
type="text"
|
||||||
|
name="Identity.Country"
|
||||||
|
[(ngModel)]="cipher.identity.country"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -210,74 +383,122 @@
|
|||||||
<div class="box" *ngIf="cipher.type === cipherType.Login">
|
<div class="box" *ngIf="cipher.type === cipherType.Login">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<ng-container *ngIf="cipher.login.hasUris">
|
<ng-container *ngIf="cipher.login.hasUris">
|
||||||
<div class="box-content-row box-content-row-multi" appBoxRow
|
<div
|
||||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy:trackByFunction">
|
class="box-content-row box-content-row-multi"
|
||||||
|
appBoxRow
|
||||||
|
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
||||||
|
>
|
||||||
<a href="#" appStopClick (click)="removeUri(u)" appA11yTitle="{{ 'remove' | i18n }}">
|
<a href="#" appStopClick (click)="removeUri(u)" appA11yTitle="{{ 'remove' | i18n }}">
|
||||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true" role="button"></i>
|
<i class="fa fa-minus-circle fa-lg" aria-hidden="true" role="button"></i>
|
||||||
</a>
|
</a>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="loginUri{{i}}">{{'uriPosition' | i18n : (i + 1)}}</label>
|
<label for="loginUri{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
||||||
<input id="loginUri{{i}}" type="text" name="Login.Uris[{{i}}].Uri" [(ngModel)]="u.uri"
|
<input
|
||||||
placeholder="{{'ex' | i18n}} https://google.com" appInputVerbatim>
|
id="loginUri{{ i }}"
|
||||||
|
type="text"
|
||||||
|
name="Login.Uris[{{ i }}].Uri"
|
||||||
|
[(ngModel)]="u.uri"
|
||||||
|
placeholder="{{ 'ex' | i18n }} https://google.com"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
<label for="loginUriMatch{{ i }}" class="sr-only">
|
<label for="loginUriMatch{{ i }}" class="sr-only">
|
||||||
{{'matchDetection' | i18n}} {{(i + 1)}}
|
{{ "matchDetection" | i18n }} {{ i + 1 }}
|
||||||
</label>
|
</label>
|
||||||
<select id="loginUriMatch{{i}}" name="Login.Uris[{{i}}].Match" [(ngModel)]="u.match"
|
<select
|
||||||
|
id="loginUriMatch{{ i }}"
|
||||||
|
name="Login.Uris[{{ i }}].Match"
|
||||||
|
[(ngModel)]="u.match"
|
||||||
[hidden]="u.showOptions === false || (u.showOptions == null && u.match == null)"
|
[hidden]="u.showOptions === false || (u.showOptions == null && u.match == null)"
|
||||||
(change)="loginUriMatchChanged(u)">
|
(change)="loginUriMatchChanged(u)"
|
||||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">{{o.name}}</option>
|
>
|
||||||
|
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">
|
||||||
|
{{ o.name }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
<a
|
||||||
appA11yTitle="{{'toggleOptions' | i18n}}" (click)="toggleUriOptions(u)">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'toggleOptions' | i18n }}"
|
||||||
|
(click)="toggleUriOptions(u)"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-cog" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-cog" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<a href="#" appStopClick appBlurClick (click)="addUri()" class="box-content-row" role="button">
|
<a
|
||||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{'newUri' | i18n}}
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
(click)="addUri()"
|
||||||
|
class="box-content-row"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{ "newUri" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="folder">{{'folder' | i18n}}</label>
|
<label for="folder">{{ "folder" | i18n }}</label>
|
||||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId">
|
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId">
|
||||||
<option *ngFor="let f of folders" [ngValue]="f.id">{{ f.name }}</option>
|
<option *ngFor="let f of folders" [ngValue]="f.id">{{ f.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="favorite">{{'favorite' | i18n}}</label>
|
<label for="favorite">{{ "favorite" | i18n }}</label>
|
||||||
<input id="favorite" type="checkbox" name="Favorite" [(ngModel)]="cipher.favorite">
|
<input id="favorite" type="checkbox" name="Favorite" [(ngModel)]="cipher.favorite" />
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="canUseReprompt">
|
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="canUseReprompt">
|
||||||
<label for="passwordPrompt">{{'passwordPrompt' | i18n}}
|
<label for="passwordPrompt"
|
||||||
|
>{{ "passwordPrompt" | i18n }}
|
||||||
<a href="#" appA11yTitle="{{ 'learnMore' | i18n }}" (click)="openHelpReprompt()">
|
<a href="#" appA11yTitle="{{ 'learnMore' | i18n }}" (click)="openHelpReprompt()">
|
||||||
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</label>
|
</label>
|
||||||
<input id="passwordPrompt" type="checkbox" name="PasswordPrompt" [ngModel]="reprompt"
|
<input
|
||||||
(change)="repromptChanged()">
|
id="passwordPrompt"
|
||||||
|
type="checkbox"
|
||||||
|
name="PasswordPrompt"
|
||||||
|
[ngModel]="reprompt"
|
||||||
|
(change)="repromptChanged()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick
|
<a
|
||||||
(click)="attachments()" *ngIf="editMode && !cloneMode" role="button">
|
class="box-content-row box-content-row-flex text-default"
|
||||||
<div class="row-main">{{'attachments' | i18n}}</div>
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
(click)="attachments()"
|
||||||
|
*ngIf="editMode && !cloneMode"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<div class="row-main">{{ "attachments" | i18n }}</div>
|
||||||
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick
|
<a
|
||||||
(click)="editCollections()" *ngIf="editMode && !cloneMode && cipher.organizationId"
|
class="box-content-row box-content-row-flex text-default"
|
||||||
role="button">
|
href="#"
|
||||||
<div class="row-main">{{'collections' | i18n}}</div>
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
(click)="editCollections()"
|
||||||
|
*ngIf="editMode && !cloneMode && cipher.organizationId"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<div class="row-main">{{ "collections" | i18n }}</div>
|
||||||
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<label for="notes">{{'notes' | i18n}}</label>
|
<label for="notes">{{ "notes" | i18n }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
@ -285,17 +506,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-vault-add-edit-custom-fields [cipher]="cipher" [thisCipherType]="cipher.type" [editMode]="editMode">
|
<app-vault-add-edit-custom-fields
|
||||||
|
[cipher]="cipher"
|
||||||
|
[thisCipherType]="cipher.type"
|
||||||
|
[editMode]="editMode"
|
||||||
|
>
|
||||||
</app-vault-add-edit-custom-fields>
|
</app-vault-add-edit-custom-fields>
|
||||||
<div class="box" *ngIf="allowOwnershipOptions()">
|
<div class="box" *ngIf="allowOwnershipOptions()">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'ownership' | i18n}}
|
{{ "ownership" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="organizationId">{{'whoOwnsThisItem' | i18n}}</label>
|
<label for="organizationId">{{ "whoOwnsThisItem" | i18n }}</label>
|
||||||
<select id="organizationId" class="form-control" name="OrganizationId"
|
<select
|
||||||
[(ngModel)]="cipher.organizationId" (change)="organizationChanged()">
|
id="organizationId"
|
||||||
|
class="form-control"
|
||||||
|
name="OrganizationId"
|
||||||
|
[(ngModel)]="cipher.organizationId"
|
||||||
|
(change)="organizationChanged()"
|
||||||
|
>
|
||||||
<option *ngFor="let o of ownershipOptions" [ngValue]="o.value">{{ o.name }}</option>
|
<option *ngFor="let o of ownershipOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -303,40 +533,70 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="(!editMode || cloneMode) && cipher.organizationId">
|
<div class="box" *ngIf="(!editMode || cloneMode) && cipher.organizationId">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'collections' | i18n}}
|
{{ "collections" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||||
{{'noCollectionsInList' | i18n}}
|
{{ "noCollectionsInList" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="collections && collections.length">
|
<div class="box-content" *ngIf="collections && collections.length">
|
||||||
<div class="box-content-row box-content-row-checkbox" *ngFor="let c of collections; let i = index"
|
<div
|
||||||
appBoxRow>
|
class="box-content-row box-content-row-checkbox"
|
||||||
|
*ngFor="let c of collections; let i = index"
|
||||||
|
appBoxRow
|
||||||
|
>
|
||||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
<input
|
||||||
name="Collection[{{i}}].Checked">
|
id="collection_{{ i }}"
|
||||||
|
type="checkbox"
|
||||||
|
[(ngModel)]="c.checked"
|
||||||
|
name="Collection[{{ i }}].Checked"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}" [disabled]="form.loading">
|
<button
|
||||||
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary"
|
||||||
|
appA11yTitle="{{ 'save' | i18n }}"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
>
|
||||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button appBlurClick type="button" (click)="cancel()">
|
<button appBlurClick type="button" (click)="cancel()">
|
||||||
{{'cancel' | i18n}}
|
{{ "cancel" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button appBlurClick type="button" (click)="share()" appA11yTitle="{{'moveToOrganization' | i18n}}"
|
<button
|
||||||
*ngIf="editMode && cipher && !cipher.organizationId && !cloneMode">
|
appBlurClick
|
||||||
|
type="button"
|
||||||
|
(click)="share()"
|
||||||
|
appA11yTitle="{{ 'moveToOrganization' | i18n }}"
|
||||||
|
*ngIf="editMode && cipher && !cipher.organizationId && !cloneMode"
|
||||||
|
>
|
||||||
<i class="fa fa-arrow-circle-o-right fa-lg fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-arrow-circle-o-right fa-lg fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
<button
|
||||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode && !cloneMode" [disabled]="deleteBtn.loading"
|
#deleteBtn
|
||||||
[appApiAction]="deletePromise">
|
appBlurClick
|
||||||
|
type="button"
|
||||||
|
(click)="delete()"
|
||||||
|
class="danger"
|
||||||
|
appA11yTitle="{{ 'delete' | i18n }}"
|
||||||
|
*ngIf="editMode && !cloneMode"
|
||||||
|
[disabled]="deleteBtn.loading"
|
||||||
|
[appApiAction]="deletePromise"
|
||||||
|
>
|
||||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading" aria-hidden="true"></i>
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!deleteBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,56 +1,71 @@
|
|||||||
import {
|
import { Component, NgZone, OnChanges, OnDestroy, ViewChild } from "@angular/core";
|
||||||
Component,
|
import { NgForm } from "@angular/forms";
|
||||||
NgZone,
|
|
||||||
OnChanges,
|
|
||||||
OnDestroy,
|
|
||||||
ViewChild
|
|
||||||
} from '@angular/core';
|
|
||||||
import { NgForm } from '@angular/forms';
|
|
||||||
|
|
||||||
import { AuditService } from 'jslib-common/abstractions/audit.service';
|
import { AuditService } from "jslib-common/abstractions/audit.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/add-edit.component';
|
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/add-edit.component";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'AddEditComponent';
|
const BroadcasterSubscriptionId = "AddEditComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-add-edit',
|
selector: "app-vault-add-edit",
|
||||||
templateUrl: 'add-edit.component.html',
|
templateUrl: "add-edit.component.html",
|
||||||
})
|
})
|
||||||
export class AddEditComponent extends BaseAddEditComponent implements OnChanges, OnDestroy {
|
export class AddEditComponent extends BaseAddEditComponent implements OnChanges, OnDestroy {
|
||||||
@ViewChild('form')
|
@ViewChild("form")
|
||||||
private form: NgForm;
|
private form: NgForm;
|
||||||
constructor(cipherService: CipherService, folderService: FolderService,
|
constructor(
|
||||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
cipherService: CipherService,
|
||||||
auditService: AuditService, stateService: StateService,
|
folderService: FolderService,
|
||||||
collectionService: CollectionService, messagingService: MessagingService,
|
i18nService: I18nService,
|
||||||
eventService: EventService, policyService: PolicyService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
passwordRepromptService: PasswordRepromptService, private broadcasterService: BroadcasterService,
|
auditService: AuditService,
|
||||||
private ngZone: NgZone, logService: LogService,
|
stateService: StateService,
|
||||||
organizationService: OrganizationService) {
|
collectionService: CollectionService,
|
||||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
messagingService: MessagingService,
|
||||||
collectionService, messagingService, eventService, policyService, logService,
|
eventService: EventService,
|
||||||
passwordRepromptService, organizationService);
|
policyService: PolicyService,
|
||||||
|
passwordRepromptService: PasswordRepromptService,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
private ngZone: NgZone,
|
||||||
|
logService: LogService,
|
||||||
|
organizationService: OrganizationService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
cipherService,
|
||||||
|
folderService,
|
||||||
|
i18nService,
|
||||||
|
platformUtilsService,
|
||||||
|
auditService,
|
||||||
|
stateService,
|
||||||
|
collectionService,
|
||||||
|
messagingService,
|
||||||
|
eventService,
|
||||||
|
policyService,
|
||||||
|
logService,
|
||||||
|
passwordRepromptService,
|
||||||
|
organizationService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'windowHidden':
|
case "windowHidden":
|
||||||
this.onWindowHidden();
|
this.onWindowHidden();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -70,8 +85,10 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges,
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
if (document.querySelectorAll('app-vault-add-edit .ng-dirty').length === 0 ||
|
if (
|
||||||
(this.cipher != null && this.cipherId !== this.cipher.id)) {
|
document.querySelectorAll("app-vault-add-edit .ng-dirty").length === 0 ||
|
||||||
|
(this.cipher != null && this.cipherId !== this.cipher.id)
|
||||||
|
) {
|
||||||
this.cipher = null;
|
this.cipher = null;
|
||||||
}
|
}
|
||||||
super.load();
|
super.load();
|
||||||
@ -82,22 +99,27 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges,
|
|||||||
this.showCardNumber = false;
|
this.showCardNumber = false;
|
||||||
this.showCardCode = false;
|
this.showCardCode = false;
|
||||||
if (this.cipher !== null && this.cipher.hasFields) {
|
if (this.cipher !== null && this.cipher.hasFields) {
|
||||||
this.cipher.fields.forEach(field => {
|
this.cipher.fields.forEach((field) => {
|
||||||
field.showValue = false;
|
field.showValue = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allowOwnershipOptions(): boolean {
|
allowOwnershipOptions(): boolean {
|
||||||
return (!this.editMode || this.cloneMode) && this.ownershipOptions
|
return (
|
||||||
&& (this.ownershipOptions.length > 1 || !this.allowPersonal);
|
(!this.editMode || this.cloneMode) &&
|
||||||
|
this.ownershipOptions &&
|
||||||
|
(this.ownershipOptions.length > 1 || !this.allowPersonal)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
markPasswordAsDirty() {
|
markPasswordAsDirty() {
|
||||||
this.form.controls['Login.Password'].markAsDirty();
|
this.form.controls["Login.Password"].markAsDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
openHelpReprompt() {
|
openHelpReprompt() {
|
||||||
this.platformUtilsService.launchUri('https://bitwarden.com/help/article/managing-items/#protect-individual-items');
|
this.platformUtilsService.launchUri(
|
||||||
|
"https://bitwarden.com/help/article/managing-items/#protect-individual-items"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box" *ngIf="cipher && cipher.hasAttachments">
|
<div class="box" *ngIf="cipher && cipher.hasAttachments">
|
||||||
<div class="box-header" id="attachmentsTitle">
|
<div class="box-header" id="attachmentsTitle">
|
||||||
{{'attachments' | i18n}}
|
{{ "attachments" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content no-hover">
|
<div class="box-content no-hover">
|
||||||
<div class="box-content-row box-content-row-flex" *ngFor="let a of cipher.attachments">
|
<div class="box-content-row box-content-row-flex" *ngFor="let a of cipher.attachments">
|
||||||
@ -13,13 +13,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<small class="row-sub-label">{{ a.sizeName }}</small>
|
<small class="row-sub-label">{{ a.sizeName }}</small>
|
||||||
<div class="action-buttons no-pad">
|
<div class="action-buttons no-pad">
|
||||||
<button class="row-btn btn" type="button" appStopClick appBlurClick
|
<button
|
||||||
appA11yTitle="{{'delete' | i18n}}" (click)="delete(a)" #deleteBtn
|
class="row-btn btn"
|
||||||
[appApiAction]="deletePromises[a.id]" [disabled]="deleteBtn.loading">
|
type="button"
|
||||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"
|
appStopClick
|
||||||
aria-hidden="true"></i>
|
appBlurClick
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
|
appA11yTitle="{{ 'delete' | i18n }}"
|
||||||
aria-hidden="true"></i>
|
(click)="delete(a)"
|
||||||
|
#deleteBtn
|
||||||
|
[appApiAction]="deletePromises[a.id]"
|
||||||
|
[disabled]="deleteBtn.loading"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-trash-o fa-lg fa-fw"
|
||||||
|
[hidden]="deleteBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!deleteBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -27,26 +41,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'newAttachment' | i18n}}
|
{{ "newAttachment" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content no-hover">
|
<div class="box-content no-hover">
|
||||||
<div class="box-content-row">
|
<div class="box-content-row">
|
||||||
<label for="file">{{'file' | i18n}}</label>
|
<label for="file">{{ "file" | i18n }}</label>
|
||||||
<input type="file" id="file" name="file" required>
|
<input type="file" id="file" name="file" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'maxFileSize' | i18n}}
|
{{ "maxFileSize" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
<button
|
||||||
[disabled]="form.loading">
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary"
|
||||||
|
appA11yTitle="{{ 'save' | i18n }}"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
>
|
||||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!form.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,25 +1,38 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
import { AttachmentsComponent as BaseAttachmentsComponent } from 'jslib-angular/components/attachments.component';
|
import { AttachmentsComponent as BaseAttachmentsComponent } from "jslib-angular/components/attachments.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-attachments',
|
selector: "app-vault-attachments",
|
||||||
templateUrl: 'attachments.component.html',
|
templateUrl: "attachments.component.html",
|
||||||
})
|
})
|
||||||
export class AttachmentsComponent extends BaseAttachmentsComponent {
|
export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
constructor(
|
||||||
cryptoService: CryptoService, platformUtilsService: PlatformUtilsService,
|
cipherService: CipherService,
|
||||||
apiService: ApiService, logService: LogService,
|
i18nService: I18nService,
|
||||||
stateService: StateService) {
|
cryptoService: CryptoService,
|
||||||
super(cipherService, i18nService, cryptoService, platformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
apiService, window, logService, stateService);
|
apiService: ApiService,
|
||||||
|
logService: LogService,
|
||||||
|
stateService: StateService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
cipherService,
|
||||||
|
i18nService,
|
||||||
|
cryptoService,
|
||||||
|
platformUtilsService,
|
||||||
|
apiService,
|
||||||
|
window,
|
||||||
|
logService,
|
||||||
|
stateService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,36 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<cdk-virtual-scroll-viewport itemSize="42" minBufferPx="400" maxBufferPx="600" *ngIf="ciphers.length">
|
<cdk-virtual-scroll-viewport
|
||||||
|
itemSize="42"
|
||||||
|
minBufferPx="400"
|
||||||
|
maxBufferPx="600"
|
||||||
|
*ngIf="ciphers.length"
|
||||||
|
>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<a *cdkVirtualFor="let c of ciphers; trackBy: trackByFn" appStopClick (click)="selectCipher(c)"
|
<a
|
||||||
(contextmenu)="rightClickCipher(c)" href="#" title="{{'viewItem' | i18n}}"
|
*cdkVirtualFor="let c of ciphers; trackBy: trackByFn"
|
||||||
[ngClass]="{'active': c.id === activeCipherId}" class="flex-list-item virtual-scroll-item">
|
appStopClick
|
||||||
|
(click)="selectCipher(c)"
|
||||||
|
(contextmenu)="rightClickCipher(c)"
|
||||||
|
href="#"
|
||||||
|
title="{{ 'viewItem' | i18n }}"
|
||||||
|
[ngClass]="{ active: c.id === activeCipherId }"
|
||||||
|
class="flex-list-item virtual-scroll-item"
|
||||||
|
>
|
||||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||||
<div class="flex-cipher-list-item">
|
<div class="flex-cipher-list-item">
|
||||||
<span class="text">
|
<span class="text">
|
||||||
{{ c.name }}
|
{{ c.name }}
|
||||||
<ng-container *ngIf="c.organizationId">
|
<ng-container *ngIf="c.organizationId">
|
||||||
<i class="fa fa-cube text-muted" title="{{ 'shared' | i18n }}" aria-hidden="true"></i>
|
<i class="fa fa-cube text-muted" title="{{ 'shared' | i18n }}" aria-hidden="true"></i>
|
||||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="c.hasAttachments">
|
<ng-container *ngIf="c.hasAttachments">
|
||||||
<i class="fa fa-paperclip text-muted" title="{{'attachments' | i18n}}" aria-hidden="true"></i>
|
<i
|
||||||
<span class="sr-only">{{'attachments' | i18n}}</span>
|
class="fa fa-paperclip text-muted"
|
||||||
|
title="{{ 'attachments' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "attachments" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="c.subTitle" class="detail">{{ c.subTitle }}</span>
|
<span *ngIf="c.subTitle" class="detail">{{ c.subTitle }}</span>
|
||||||
@ -26,14 +42,20 @@
|
|||||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||||
<ng-container *ngIf="loaded">
|
<ng-container *ngIf="loaded">
|
||||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||||
<p>{{'noItemsInList' | i18n}}</p>
|
<p>{{ "noItemsInList" | i18n }}</p>
|
||||||
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
|
<button (click)="addCipher()" class="btn block primary link">{{ "addItem" | i18n }}</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button appBlurClick (click)="addCipher()" (contextmenu)="addCipherOptions()"
|
<button
|
||||||
class="block primary" appA11yTitle="{{'addItem' | i18n}}" [disabled]="deleted">
|
appBlurClick
|
||||||
|
(click)="addCipher()"
|
||||||
|
(contextmenu)="addCipherOptions()"
|
||||||
|
class="block primary"
|
||||||
|
appA11yTitle="{{ 'addItem' | i18n }}"
|
||||||
|
[disabled]="deleted"
|
||||||
|
>
|
||||||
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { CiphersComponent as BaseCiphersComponent } from 'jslib-angular/components/ciphers.component';
|
import { CiphersComponent as BaseCiphersComponent } from "jslib-angular/components/ciphers.component";
|
||||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||||
|
|
||||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
import { SearchBarService } from '../layout/search/search-bar.service';
|
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-ciphers',
|
selector: "app-vault-ciphers",
|
||||||
templateUrl: 'ciphers.component.html',
|
templateUrl: "ciphers.component.html",
|
||||||
})
|
})
|
||||||
export class CiphersComponent extends BaseCiphersComponent {
|
export class CiphersComponent extends BaseCiphersComponent {
|
||||||
|
|
||||||
constructor(searchService: SearchService, searchBarService: SearchBarService) {
|
constructor(searchService: SearchService, searchBarService: SearchBarService) {
|
||||||
super(searchService);
|
super(searchService);
|
||||||
|
|
||||||
searchBarService.searchText.subscribe(searchText => {
|
searchBarService.searchText.subscribe((searchText) => {
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
this.search(200);
|
this.search(200);
|
||||||
});
|
});
|
||||||
|
@ -4,28 +4,44 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="collectionsTitle">
|
<div class="box-header" id="collectionsTitle">
|
||||||
{{'collections' | i18n}}
|
{{ "collections" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||||
{{'noCollectionsInList' | i18n}}
|
{{ "noCollectionsInList" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="collections && collections.length">
|
<div class="box-content" *ngIf="collections && collections.length">
|
||||||
<div class="box-content-row box-content-row-checkbox"
|
<div
|
||||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
class="box-content-row box-content-row-checkbox"
|
||||||
|
*ngFor="let c of collections; let i = index"
|
||||||
|
appBoxRow
|
||||||
|
>
|
||||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
<input
|
||||||
name="Collection[{{i}}].Checked">
|
id="collection_{{ i }}"
|
||||||
|
type="checkbox"
|
||||||
|
[(ngModel)]="c.checked"
|
||||||
|
name="Collection[{{ i }}].Checked"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
<button
|
||||||
[disabled]="form.loading">
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary"
|
||||||
|
appA11yTitle="{{ 'save' | i18n }}"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
>
|
||||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!form.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,25 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import { CollectionsComponent as BaseCollectionsComponent } from 'jslib-angular/components/collections.component';
|
import { CollectionsComponent as BaseCollectionsComponent } from "jslib-angular/components/collections.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-collections',
|
selector: "app-vault-collections",
|
||||||
templateUrl: 'collections.component.html',
|
templateUrl: "collections.component.html",
|
||||||
})
|
})
|
||||||
export class CollectionsComponent extends BaseCollectionsComponent {
|
export class CollectionsComponent extends BaseCollectionsComponent {
|
||||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
constructor(
|
||||||
collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
|
cipherService: CipherService,
|
||||||
logService: LogService) {
|
i18nService: I18nService,
|
||||||
|
collectionService: CollectionService,
|
||||||
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
super(collectionService, platformUtilsService, i18nService, cipherService, logService);
|
super(collectionService, platformUtilsService, i18nService, cipherService, logService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,20 @@
|
|||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<form class="modal-content" #form (ngSubmit)="submit()" [formGroup]="exportForm">
|
<form class="modal-content" #form (ngSubmit)="submit()" [formGroup]="exportForm">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<app-callout type="warning" title="{{'vaultExportDisabled' | i18n}}" *ngIf="disabledByPolicy">
|
<app-callout
|
||||||
{{'personalVaultExportPolicyInEffect' | i18n}}
|
type="warning"
|
||||||
|
title="{{ 'vaultExportDisabled' | i18n }}"
|
||||||
|
*ngIf="disabledByPolicy"
|
||||||
|
>
|
||||||
|
{{ "personalVaultExportPolicyInEffect" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="exportTitle">
|
<div class="box-header" id="exportTitle">
|
||||||
{{'exportVault' | i18n}}
|
{{ "exportVault" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="format">{{'fileFormat' | i18n}}</label>
|
<label for="format">{{ "fileFormat" | i18n }}</label>
|
||||||
<select class="form-control" id="format" name="Format" formControlName="format">
|
<select class="form-control" id="format" name="Format" formControlName="format">
|
||||||
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
|
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
@ -20,16 +24,21 @@
|
|||||||
</app-verify-master-password>
|
</app-verify-master-password>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<p>{{'confirmIdentity' | i18n}}</p>
|
<p>{{ "confirmIdentity" | i18n }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'submit' | i18n}}"
|
<button
|
||||||
[disabled]="disabledByPolicy">
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary"
|
||||||
|
appA11yTitle="{{ 'submit' | i18n }}"
|
||||||
|
[disabled]="disabledByPolicy"
|
||||||
|
>
|
||||||
<i class="fa fa-download fa-lg fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-download fa-lg fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,37 +1,51 @@
|
|||||||
import {
|
import { Component, OnInit } from "@angular/core";
|
||||||
Component,
|
import { FormBuilder } from "@angular/forms";
|
||||||
OnInit,
|
|
||||||
} from '@angular/core';
|
|
||||||
import { FormBuilder } from '@angular/forms';
|
|
||||||
|
|
||||||
import * as os from 'os';
|
import * as os from "os";
|
||||||
|
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
import { ExportService } from 'jslib-common/abstractions/export.service';
|
import { ExportService } from "jslib-common/abstractions/export.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.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";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'ExportComponent';
|
const BroadcasterSubscriptionId = "ExportComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-export',
|
selector: "app-export",
|
||||||
templateUrl: 'export.component.html',
|
templateUrl: "export.component.html",
|
||||||
})
|
})
|
||||||
export class ExportComponent extends BaseExportComponent implements OnInit {
|
export class ExportComponent extends BaseExportComponent implements OnInit {
|
||||||
constructor(cryptoService: CryptoService, i18nService: I18nService,
|
constructor(
|
||||||
platformUtilsService: PlatformUtilsService, exportService: ExportService,
|
cryptoService: CryptoService,
|
||||||
eventService: EventService, policyService: PolicyService,
|
i18nService: I18nService,
|
||||||
userVerificationService: UserVerificationService, fb: FormBuilder,
|
platformUtilsService: PlatformUtilsService,
|
||||||
private broadcasterService: BroadcasterService, logService: LogService) {
|
exportService: ExportService,
|
||||||
super(cryptoService, i18nService, platformUtilsService, exportService, eventService,
|
eventService: EventService,
|
||||||
policyService, window, logService, userVerificationService, fb);
|
policyService: PolicyService,
|
||||||
|
userVerificationService: UserVerificationService,
|
||||||
|
fb: FormBuilder,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
cryptoService,
|
||||||
|
i18nService,
|
||||||
|
platformUtilsService,
|
||||||
|
exportService,
|
||||||
|
eventService,
|
||||||
|
policyService,
|
||||||
|
window,
|
||||||
|
logService,
|
||||||
|
userVerificationService,
|
||||||
|
fb
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -41,17 +55,24 @@ export class ExportComponent extends BaseExportComponent implements OnInit {
|
|||||||
async warningDialog() {
|
async warningDialog() {
|
||||||
if (this.encryptedFormat) {
|
if (this.encryptedFormat) {
|
||||||
return await this.platformUtilsService.showDialog(
|
return await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('encExportKeyWarningDesc') +
|
this.i18nService.t("encExportKeyWarningDesc") +
|
||||||
os.EOL + os.EOL +
|
os.EOL +
|
||||||
this.i18nService.t('encExportAccountWarningDesc'),
|
os.EOL +
|
||||||
this.i18nService.t('confirmVaultExport'), this.i18nService.t('exportVault'),
|
this.i18nService.t("encExportAccountWarningDesc"),
|
||||||
this.i18nService.t('cancel'), 'warning',
|
this.i18nService.t("confirmVaultExport"),
|
||||||
true);
|
this.i18nService.t("exportVault"),
|
||||||
|
this.i18nService.t("cancel"),
|
||||||
|
"warning",
|
||||||
|
true
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return await this.platformUtilsService.showDialog(
|
return await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('exportWarningDesc'),
|
this.i18nService.t("exportWarningDesc"),
|
||||||
this.i18nService.t('confirmVaultExport'), this.i18nService.t('exportVault'),
|
this.i18nService.t("confirmVaultExport"),
|
||||||
this.i18nService.t('cancel'), 'warning');
|
this.i18nService.t("exportVault"),
|
||||||
|
this.i18nService.t("cancel"),
|
||||||
|
"warning"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,27 +8,56 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="name">{{'name' | i18n}}</label>
|
<label for="name">{{ "name" | i18n }}</label>
|
||||||
<input id="name" type="text" name="Name" [(ngModel)]="folder.name"
|
<input
|
||||||
[appAutofocus]="!editMode">
|
id="name"
|
||||||
|
type="text"
|
||||||
|
name="Name"
|
||||||
|
[(ngModel)]="folder.name"
|
||||||
|
[appAutofocus]="!editMode"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
<button
|
||||||
[disabled]="form.loading">
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary"
|
||||||
|
appA11yTitle="{{ 'save' | i18n }}"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
>
|
||||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!form.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
<button
|
||||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
|
#deleteBtn
|
||||||
[appApiAction]="deletePromise">
|
appBlurClick
|
||||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
type="button"
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
|
(click)="delete()"
|
||||||
aria-hidden="true"></i>
|
class="danger"
|
||||||
|
appA11yTitle="{{ 'delete' | i18n }}"
|
||||||
|
*ngIf="editMode"
|
||||||
|
[disabled]="deleteBtn.loading"
|
||||||
|
[appApiAction]="deletePromise"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-trash-o fa-lg fa-fw"
|
||||||
|
[hidden]="deleteBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!deleteBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import {
|
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "jslib-angular/components/folder-add-edit.component";
|
||||||
FolderAddEditComponent as BaseFolderAddEditComponent,
|
|
||||||
} from 'jslib-angular/components/folder-add-edit.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-folder-add-edit',
|
selector: "app-folder-add-edit",
|
||||||
templateUrl: 'folder-add-edit.component.html',
|
templateUrl: "folder-add-edit.component.html",
|
||||||
})
|
})
|
||||||
export class FolderAddEditComponent extends BaseFolderAddEditComponent {
|
export class FolderAddEditComponent extends BaseFolderAddEditComponent {
|
||||||
constructor(folderService: FolderService, i18nService: I18nService,
|
constructor(
|
||||||
platformUtilsService: PlatformUtilsService, logService: LogService) {
|
folderService: FolderService,
|
||||||
|
i18nService: I18nService,
|
||||||
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
logService: LogService
|
||||||
|
) {
|
||||||
super(folderService, i18nService, platformUtilsService, logService);
|
super(folderService, i18nService, platformUtilsService, logService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,98 +1,140 @@
|
|||||||
<div class="mac-bar"></div>
|
<div class="mac-bar"></div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="inner-content">
|
<div class="inner-content">
|
||||||
<h2 class="sr-only">{{'filters' | i18n}}</h2>
|
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li [ngClass]="{ active: selectedAll }">
|
<li [ngClass]="{ active: selectedAll }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||||
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{'allItems' | i18n}}
|
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{ "allItems" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{ active: selectedFavorites }">
|
<li [ngClass]="{ active: selectedFavorites }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectFavorites()">
|
<a href="#" appStopClick appBlurClick (click)="selectFavorites()">
|
||||||
<i class="fa fa-fw fa-star" aria-hidden="true"></i> {{'favorites' | i18n}}
|
<i class="fa fa-fw fa-star" aria-hidden="true"></i> {{ "favorites" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{ active: selectedTrash }">
|
<li [ngClass]="{ active: selectedTrash }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectTrash()">
|
<a href="#" appStopClick appBlurClick (click)="selectTrash()">
|
||||||
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> {{'trash' | i18n}}
|
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> {{ "trash" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2>{{'types' | i18n}}</h2>
|
<h2>{{ "types" | i18n }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li [ngClass]="{ active: selectedType === cipherType.Login }">
|
<li [ngClass]="{ active: selectedType === cipherType.Login }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
||||||
<i class="fa fa-fw fa-globe" aria-hidden="true"></i> {{'typeLogin' | i18n}}
|
<i class="fa fa-fw fa-globe" aria-hidden="true"></i> {{ "typeLogin" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{ active: selectedType === cipherType.Card }">
|
<li [ngClass]="{ active: selectedType === cipherType.Card }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
||||||
<i class="fa fa-fw fa-credit-card" aria-hidden="true"></i> {{'typeCard' | i18n}}
|
<i class="fa fa-fw fa-credit-card" aria-hidden="true"></i> {{ "typeCard" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{ active: selectedType === cipherType.Identity }">
|
<li [ngClass]="{ active: selectedType === cipherType.Identity }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
||||||
<i class="fa fa-fw fa-id-card-o" aria-hidden="true"></i> {{'typeIdentity' | i18n}}
|
<i class="fa fa-fw fa-id-card-o" aria-hidden="true"></i> {{ "typeIdentity" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{ active: selectedType === cipherType.SecureNote }">
|
<li [ngClass]="{ active: selectedType === cipherType.SecureNote }">
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
||||||
<i class="fa fa-fw fa-sticky-note-o" aria-hidden="true"></i> {{'typeSecureNote' | i18n}}
|
<i class="fa fa-fw fa-sticky-note-o" aria-hidden="true"></i> {{
|
||||||
|
"typeSecureNote" | i18n
|
||||||
|
}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p *ngIf="!loaded" class="text-muted">{{'loading' | i18n}}</p>
|
<p *ngIf="!loaded" class="text-muted">{{ "loading" | i18n }}</p>
|
||||||
<ng-container *ngIf="loaded">
|
<ng-container *ngIf="loaded">
|
||||||
<div class="heading">
|
<div class="heading">
|
||||||
<h2>{{'folders' | i18n}}</h2>
|
<h2>{{ "folders" | i18n }}</h2>
|
||||||
<button appBlurClick (click)="addFolder()" appA11yTitle="{{ 'addFolder' | i18n }}">
|
<button appBlurClick (click)="addFolder()" appA11yTitle="{{ 'addFolder' | i18n }}">
|
||||||
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<ng-template #recursiveFolders let-folders>
|
<ng-template #recursiveFolders let-folders>
|
||||||
<li *ngFor="let f of folders"
|
<li
|
||||||
[ngClass]="{active: selectedFolder && f.node.id === selectedFolderId}">
|
*ngFor="let f of folders"
|
||||||
|
[ngClass]="{ active: selectedFolder && f.node.id === selectedFolderId }"
|
||||||
|
>
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectFolder(f.node)">
|
<a href="#" appStopClick appBlurClick (click)="selectFolder(f.node)">
|
||||||
<i *ngIf="f.children.length" class="fa-fw fa" title="{{'toggleCollapse' | i18n}}" aria-hidden="true"
|
<i
|
||||||
[ngClass]="{'fa-caret-right': isCollapsed(f.node), 'fa-caret-down': !isCollapsed(f.node)}"
|
*ngIf="f.children.length"
|
||||||
(click)="collapse(f.node)" appStopProp></i>
|
class="fa-fw fa"
|
||||||
<i *ngIf="f.children.length === 0" class="fa-fw fa fa-folder-o" aria-hidden="true"></i>
|
title="{{ 'toggleCollapse' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{
|
||||||
|
'fa-caret-right': isCollapsed(f.node),
|
||||||
|
'fa-caret-down': !isCollapsed(f.node)
|
||||||
|
}"
|
||||||
|
(click)="collapse(f.node)"
|
||||||
|
appStopProp
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
*ngIf="f.children.length === 0"
|
||||||
|
class="fa-fw fa fa-folder-o"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
{{ f.node.name }}
|
{{ f.node.name }}
|
||||||
<span appStopProp appStopClick (click)="editFolder(f.node)" role="button"
|
<span
|
||||||
appA11yTitle="{{'editFolder' | i18n}}" *ngIf="f.node.id">
|
appStopProp
|
||||||
|
appStopClick
|
||||||
|
(click)="editFolder(f.node)"
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'editFolder' | i18n }}"
|
||||||
|
*ngIf="f.node.id"
|
||||||
|
>
|
||||||
<i class="fa fa-pencil fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-pencil fa-fw" aria-hidden="true"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="fa-ul" *ngIf="f.children.length && !isCollapsed(f.node)">
|
<ul class="fa-ul" *ngIf="f.children.length && !isCollapsed(f.node)">
|
||||||
<ng-container *ngTemplateOutlet="recursiveFolders; context:{ $implicit: f.children }">
|
<ng-container
|
||||||
|
*ngTemplateOutlet="recursiveFolders; context: { $implicit: f.children }"
|
||||||
|
>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-container *ngTemplateOutlet="recursiveFolders; context:{ $implicit: nestedFolders }"></ng-container>
|
<ng-container
|
||||||
|
*ngTemplateOutlet="recursiveFolders; context: { $implicit: nestedFolders }"
|
||||||
|
></ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
<div *ngIf="collections && collections.length">
|
<div *ngIf="collections && collections.length">
|
||||||
<h2>{{'collections' | i18n}}</h2>
|
<h2>{{ "collections" | i18n }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<ng-template #recursiveCollections let-collections>
|
<ng-template #recursiveCollections let-collections>
|
||||||
<li *ngFor="let c of collections" [ngClass]="{active: c.node.id === selectedCollectionId}">
|
<li
|
||||||
|
*ngFor="let c of collections"
|
||||||
|
[ngClass]="{ active: c.node.id === selectedCollectionId }"
|
||||||
|
>
|
||||||
<a href="#" appStopClick appBlurClick (click)="selectCollection(c.node)">
|
<a href="#" appStopClick appBlurClick (click)="selectCollection(c.node)">
|
||||||
<i *ngIf="c.children.length" class="fa-fw fa" title="{{'toggleCollapse' | i18n}}" aria-hidden="true"
|
<i
|
||||||
[ngClass]="{'fa-caret-right': isCollapsed(c.node), 'fa-caret-down': !isCollapsed(c.node)}"
|
*ngIf="c.children.length"
|
||||||
(click)="collapse(c.node)" appStopProp></i>
|
class="fa-fw fa"
|
||||||
|
title="{{ 'toggleCollapse' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{
|
||||||
|
'fa-caret-right': isCollapsed(c.node),
|
||||||
|
'fa-caret-down': !isCollapsed(c.node)
|
||||||
|
}"
|
||||||
|
(click)="collapse(c.node)"
|
||||||
|
appStopProp
|
||||||
|
></i>
|
||||||
<i *ngIf="c.children.length === 0" class="fa-fw fa fa-cube" aria-hidden="true"></i>
|
<i *ngIf="c.children.length === 0" class="fa-fw fa fa-cube" aria-hidden="true"></i>
|
||||||
{{ c.node.name }}
|
{{ c.node.name }}
|
||||||
</a>
|
</a>
|
||||||
<ul class="fa-ul" *ngIf="c.children.length && !isCollapsed(c.node)">
|
<ul class="fa-ul" *ngIf="c.children.length && !isCollapsed(c.node)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngTemplateOutlet="recursiveCollections; context:{ $implicit: c.children }">
|
*ngTemplateOutlet="recursiveCollections; context: { $implicit: c.children }"
|
||||||
|
>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-container *ngTemplateOutlet="recursiveCollections; context:{ $implicit: nestedCollections }">
|
<ng-container
|
||||||
|
*ngTemplateOutlet="recursiveCollections; context: { $implicit: nestedCollections }"
|
||||||
|
>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
import { GroupingsComponent as BaseGroupingsComponent } from 'jslib-angular/components/groupings.component';
|
import { GroupingsComponent as BaseGroupingsComponent } from "jslib-angular/components/groupings.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-groupings',
|
selector: "app-vault-groupings",
|
||||||
templateUrl: 'groupings.component.html',
|
templateUrl: "groupings.component.html",
|
||||||
})
|
})
|
||||||
export class GroupingsComponent extends BaseGroupingsComponent {
|
export class GroupingsComponent extends BaseGroupingsComponent {
|
||||||
constructor(collectionService: CollectionService, folderService: FolderService,
|
constructor(
|
||||||
stateService: StateService) {
|
collectionService: CollectionService,
|
||||||
|
folderService: FolderService,
|
||||||
|
stateService: StateService
|
||||||
|
) {
|
||||||
super(collectionService, folderService, stateService);
|
super(collectionService, folderService, stateService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,33 +4,47 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="passwordGenHistoryTitle">
|
<div class="box-header" id="passwordGenHistoryTitle">
|
||||||
{{'passwordHistory' | i18n}}
|
{{ "passwordHistory" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<div class="password-wrapper monospaced" appSelectCopy
|
<div
|
||||||
[innerHTML]="h.password | colorPassword"></div>
|
class="password-wrapper monospaced"
|
||||||
<span class="detail">{{h.date | date:'medium'}}</span>
|
appSelectCopy
|
||||||
|
[innerHTML]="h.password | colorPassword"
|
||||||
|
></div>
|
||||||
|
<span class="detail">{{ h.date | date: "medium" }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
<a
|
||||||
(click)="copy(h.password)" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||||
|
(click)="copy(h.password)"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="!history.length">
|
<div class="box-content-row" *ngIf="!history.length">
|
||||||
{{'noPasswordsInList' | i18n}}
|
{{ "noPasswordsInList" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button appBlurClick type="button" (click)="clear()" class="danger"
|
<button
|
||||||
appA11yTitle="{{'clear' | i18n}}">
|
appBlurClick
|
||||||
|
type="button"
|
||||||
|
(click)="clear()"
|
||||||
|
class="danger"
|
||||||
|
appA11yTitle="{{ 'clear' | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import {
|
import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "jslib-angular/components/password-generator-history.component";
|
||||||
PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent,
|
|
||||||
} from 'jslib-angular/components/password-generator-history.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-password-generator-history',
|
selector: "app-password-generator-history",
|
||||||
templateUrl: 'password-generator-history.component.html',
|
templateUrl: "password-generator-history.component.html",
|
||||||
})
|
})
|
||||||
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
|
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
|
||||||
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
i18nService: I18nService) {
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
i18nService: I18nService
|
||||||
|
) {
|
||||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{'generatePassword' | i18n}}">
|
<div
|
||||||
|
class="modal fade"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
attr.aria-label="{{ 'generatePassword' | i18n }}"
|
||||||
|
>
|
||||||
<div class="modal-dialog modal-sm" role="document">
|
<div class="modal-dialog modal-sm" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
||||||
{{'passwordGeneratorPolicyInEffect' | i18n}}
|
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="password-block">
|
<div class="password-block">
|
||||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||||
@ -11,29 +16,45 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="regenerate()">
|
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="regenerate()">
|
||||||
<i class="fa fa-fw fa-refresh" aria-hidden="true"></i> {{'regeneratePassword' | i18n}}
|
<i class="fa fa-fw fa-refresh" aria-hidden="true"></i>
|
||||||
|
{{ "regeneratePassword" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="copy()">
|
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="copy()">
|
||||||
<i class="fa fa-fw fa-clone" aria-hidden="true"></i> {{'copyPassword' | i18n}}
|
<i class="fa fa-fw fa-clone" aria-hidden="true"></i> {{ "copyPassword" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<button type="button" (click)="toggleOptions()" appA11yTitle="{{'toggleVisibility' | i18n}}">
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="toggleOptions()"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-plus-square-o" aria-hidden="true" [hidden]="showOptions"></i>
|
<i class="fa fa-plus-square-o" aria-hidden="true" [hidden]="showOptions"></i>
|
||||||
<i class="fa fa-minus-square-o" aria-hidden="true" [hidden]="!showOptions"></i>
|
<i class="fa fa-minus-square-o" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||||
{{'options' | i18n}}
|
{{ "options" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content condensed" [hidden]="!showOptions">
|
<div class="box-content condensed" [hidden]="!showOptions">
|
||||||
<div class="box-content-row box-content-row-radio">
|
<div class="box-content-row box-content-row-radio">
|
||||||
<label class="sr-only radio-header">{{'type' | i18n}}</label>
|
<label class="sr-only radio-header">{{ "type" | i18n }}</label>
|
||||||
<div class="radio-group text-default" appBoxRow name="PassTypeOptions"
|
<div
|
||||||
*ngFor="let o of passTypeOptions">
|
class="radio-group text-default"
|
||||||
<input type="radio" class="radio" [(ngModel)]="options.type" name="Type_{{o.value}}"
|
appBoxRow
|
||||||
id="type_{{o.value}}" [value]="o.value" (change)="saveOptions()"
|
name="PassTypeOptions"
|
||||||
[checked]="options.type === o.value">
|
*ngFor="let o of passTypeOptions"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
class="radio"
|
||||||
|
[(ngModel)]="options.type"
|
||||||
|
name="Type_{{ o.value }}"
|
||||||
|
id="type_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[checked]="options.type === o.value"
|
||||||
|
/>
|
||||||
<label class="unstyled" for="type_{{ o.value }}">
|
<label class="unstyled" for="type_{{ o.value }}">
|
||||||
{{ o.name }}
|
{{ o.name }}
|
||||||
</label>
|
</label>
|
||||||
@ -44,24 +65,45 @@
|
|||||||
<div class="box" [hidden]="!showOptions" *ngIf="options.type === 'passphrase'">
|
<div class="box" [hidden]="!showOptions" *ngIf="options.type === 'passphrase'">
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
<label for="num-words">{{'numWords' | i18n}}</label>
|
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||||
<input id="num-words" type="number" min="3" max="20" (blur)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.numWords">
|
id="num-words"
|
||||||
|
type="number"
|
||||||
|
min="3"
|
||||||
|
max="20"
|
||||||
|
(blur)="saveOptions()"
|
||||||
|
[(ngModel)]="options.numWords"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
<label for="word-separator">{{'wordSeparator' | i18n}}</label>
|
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||||
<input id="word-separator" type="text" maxlength="1" (input)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.wordSeparator">
|
id="word-separator"
|
||||||
|
type="text"
|
||||||
|
maxlength="1"
|
||||||
|
(input)="saveOptions()"
|
||||||
|
[(ngModel)]="options.wordSeparator"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="capitalize">{{'capitalize' | i18n}}</label>
|
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||||
<input id="capitalize" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.capitalize" [disabled]="enforcedPolicyOptions?.capitalize">
|
id="capitalize"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[(ngModel)]="options.capitalize"
|
||||||
|
[disabled]="enforcedPolicyOptions?.capitalize"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="include-number">{{'includeNumber' | i18n}}</label>
|
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||||
<input id="include-number" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.includeNumber" [disabled]="enforcedPolicyOptions?.includeNumber">
|
id="include-number"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[(ngModel)]="options.includeNumber"
|
||||||
|
[disabled]="enforcedPolicyOptions?.includeNumber"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -69,61 +111,119 @@
|
|||||||
<div class="box" [hidden]="!showOptions">
|
<div class="box" [hidden]="!showOptions">
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||||
<label for="length">{{'length' | i18n}}</label>
|
<label for="length">{{ "length" | i18n }}</label>
|
||||||
<input id="length" type="number" min="5" max="128" [(ngModel)]="options.length"
|
<input
|
||||||
(blur)="saveOptions()">
|
id="length"
|
||||||
<input id="lengthRange" type="range" min="5" max="128" step="1"
|
type="number"
|
||||||
[(ngModel)]="options.length" (change)="sliderChanged()" (input)="sliderInput()">
|
min="5"
|
||||||
|
max="128"
|
||||||
|
[(ngModel)]="options.length"
|
||||||
|
(blur)="saveOptions()"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
id="lengthRange"
|
||||||
|
type="range"
|
||||||
|
min="5"
|
||||||
|
max="128"
|
||||||
|
step="1"
|
||||||
|
[(ngModel)]="options.length"
|
||||||
|
(change)="sliderChanged()"
|
||||||
|
(input)="sliderInput()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="uppercase">A-Z</label>
|
<label for="uppercase">A-Z</label>
|
||||||
<input id="uppercase" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[disabled]="enforcedPolicyOptions?.useUppercase" [(ngModel)]="options.uppercase">
|
id="uppercase"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[disabled]="enforcedPolicyOptions?.useUppercase"
|
||||||
|
[(ngModel)]="options.uppercase"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="lowercase">a-z</label>
|
<label for="lowercase">a-z</label>
|
||||||
<input id="lowercase" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[disabled]="enforcedPolicyOptions?.useLowercase" [(ngModel)]="options.lowercase">
|
id="lowercase"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[disabled]="enforcedPolicyOptions?.useLowercase"
|
||||||
|
[(ngModel)]="options.lowercase"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="numbers">0-9</label>
|
<label for="numbers">0-9</label>
|
||||||
<input id="numbers" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[disabled]="enforcedPolicyOptions?.useNumbers" [(ngModel)]="options.number">
|
id="numbers"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[disabled]="enforcedPolicyOptions?.useNumbers"
|
||||||
|
[(ngModel)]="options.number"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="special">!@#$%^&*</label>
|
<label for="special">!@#$%^&*</label>
|
||||||
<input id="special" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[disabled]="enforcedPolicyOptions?.useSpecial" [(ngModel)]="options.special">
|
id="special"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[disabled]="enforcedPolicyOptions?.useSpecial"
|
||||||
|
[(ngModel)]="options.special"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" [hidden]="!showOptions">
|
<div class="box" [hidden]="!showOptions">
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
<label for="min-number">{{'minNumbers' | i18n}}</label>
|
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||||
<input id="min-number" type="number" min="0" max="9" (blur)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.minNumber">
|
id="min-number"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="9"
|
||||||
|
(blur)="saveOptions()"
|
||||||
|
[(ngModel)]="options.minNumber"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
<label for="min-special">{{'minSpecial' | i18n}}</label>
|
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||||
<input id="min-special" type="number" min="0" max="9" (blur)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.minSpecial">
|
id="min-special"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="9"
|
||||||
|
(blur)="saveOptions()"
|
||||||
|
[(ngModel)]="options.minSpecial"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="ambiguous">{{'ambiguous' | i18n}}</label>
|
<label for="ambiguous">{{ "ambiguous" | i18n }}</label>
|
||||||
<input id="ambiguous" type="checkbox" (change)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="avoidAmbiguous">
|
id="ambiguous"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveOptions()"
|
||||||
|
[(ngModel)]="avoidAmbiguous"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="primary" appBlurClick *ngIf="showSelect" (click)="select()"
|
<button
|
||||||
appA11yTitle="{{'select' | i18n}}">
|
type="button"
|
||||||
|
class="primary"
|
||||||
|
appBlurClick
|
||||||
|
*ngIf="showSelect"
|
||||||
|
(click)="select()"
|
||||||
|
appA11yTitle="{{ 'select' | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-fw fa-check" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-fw fa-check" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{(showSelect ? 'cancel' : 'close') | i18n}}</button>
|
<button type="button" data-dismiss="modal">
|
||||||
|
{{ (showSelect ? "cancel" : "close") | i18n }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import {
|
import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component";
|
||||||
PasswordGeneratorComponent as BasePasswordGeneratorComponent,
|
|
||||||
} from 'jslib-angular/components/password-generator.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-password-generator',
|
selector: "app-password-generator",
|
||||||
templateUrl: 'password-generator.component.html',
|
templateUrl: "password-generator.component.html",
|
||||||
})
|
})
|
||||||
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
|
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
|
||||||
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
i18nService: I18nService) {
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
i18nService: I18nService
|
||||||
|
) {
|
||||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="passwordHistoryTitle">
|
<div class="box-header" id="passwordHistoryTitle">
|
||||||
{{'passwordHistory' | i18n}}
|
{{ "passwordHistory" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||||
@ -12,23 +12,29 @@
|
|||||||
<span class="text monospaced">
|
<span class="text monospaced">
|
||||||
{{ h.password }}
|
{{ h.password }}
|
||||||
</span>
|
</span>
|
||||||
<span class="detail">{{h.lastUsedDate | date:'medium'}}</span>
|
<span class="detail">{{ h.lastUsedDate | date: "medium" }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
<a
|
||||||
(click)="copy(h.password)" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||||
|
(click)="copy(h.password)"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="!history.length">
|
<div class="box-content-row" *ngIf="!history.length">
|
||||||
{{'noPasswordsInList' | i18n}}
|
{{ "noPasswordsInList" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import {
|
import { PasswordHistoryComponent as BasePasswordHistoryComponent } from "jslib-angular/components/password-history.component";
|
||||||
PasswordHistoryComponent as BasePasswordHistoryComponent,
|
|
||||||
} from 'jslib-angular/components/password-history.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-password-history',
|
selector: "app-password-history",
|
||||||
templateUrl: 'password-history.component.html',
|
templateUrl: "password-history.component.html",
|
||||||
})
|
})
|
||||||
export class PasswordHistoryComponent extends BasePasswordHistoryComponent {
|
export class PasswordHistoryComponent extends BasePasswordHistoryComponent {
|
||||||
constructor(cipherService: CipherService, platformUtilsService: PlatformUtilsService,
|
constructor(
|
||||||
i18nService: I18nService) {
|
cipherService: CipherService,
|
||||||
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
i18nService: I18nService
|
||||||
|
) {
|
||||||
super(cipherService, platformUtilsService, i18nService, window);
|
super(cipherService, platformUtilsService, i18nService, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,50 +4,71 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header" id="moveToOrgTitle">
|
<div class="box-header" id="moveToOrgTitle">
|
||||||
{{'moveToOrganization' | i18n}}
|
{{ "moveToOrganization" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||||
<div class="box-content-row">
|
<div class="box-content-row">
|
||||||
{{'noOrganizationsList' | i18n}}
|
{{ "noOrganizationsList" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row" appBoxRow>
|
||||||
<label for="organization">{{'organization' | i18n}}</label>
|
<label for="organization">{{ "organization" | i18n }}</label>
|
||||||
<select id="organization" name="OrganizationId" [(ngModel)]="organizationId"
|
<select
|
||||||
(change)="filterCollections()">
|
id="organization"
|
||||||
|
name="OrganizationId"
|
||||||
|
[(ngModel)]="organizationId"
|
||||||
|
(change)="filterCollections()"
|
||||||
|
>
|
||||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
|
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{{'moveToOrgDesc' | i18n}}
|
{{ "moveToOrgDesc" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="organizations && organizations.length">
|
<div class="box" *ngIf="organizations && organizations.length">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'collections' | i18n}}
|
{{ "collections" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||||
{{'noCollectionsInList' | i18n}}
|
{{ "noCollectionsInList" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content" *ngIf="collections && collections.length">
|
<div class="box-content" *ngIf="collections && collections.length">
|
||||||
<div class="box-content-row box-content-row-checkbox"
|
<div
|
||||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
class="box-content-row box-content-row-checkbox"
|
||||||
|
*ngFor="let c of collections; let i = index"
|
||||||
|
appBoxRow
|
||||||
|
>
|
||||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
<input
|
||||||
name="Collection[{{i}}].Checked">
|
id="collection_{{ i }}"
|
||||||
|
type="checkbox"
|
||||||
|
[(ngModel)]="c.checked"
|
||||||
|
name="Collection[{{ i }}].Checked"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
<button
|
||||||
[disabled]="form.loading || !canSave" *ngIf="organizations && organizations.length">
|
appBlurClick
|
||||||
|
type="submit"
|
||||||
|
class="primary"
|
||||||
|
appA11yTitle="{{ 'save' | i18n }}"
|
||||||
|
[disabled]="form.loading || !canSave"
|
||||||
|
*ngIf="organizations && organizations.length"
|
||||||
|
>
|
||||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
<i
|
||||||
|
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||||
|
[hidden]="!form.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,23 +1,34 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import { ShareComponent as BaseShareComponent } from 'jslib-angular/components/share.component';
|
import { ShareComponent as BaseShareComponent } from "jslib-angular/components/share.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-share',
|
selector: "app-vault-share",
|
||||||
templateUrl: 'share.component.html',
|
templateUrl: "share.component.html",
|
||||||
})
|
})
|
||||||
export class ShareComponent extends BaseShareComponent {
|
export class ShareComponent extends BaseShareComponent {
|
||||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
constructor(
|
||||||
collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
|
cipherService: CipherService,
|
||||||
logService: LogService, organizationService: OrganizationService) {
|
i18nService: I18nService,
|
||||||
super(collectionService, platformUtilsService, i18nService, cipherService,
|
collectionService: CollectionService,
|
||||||
logService, organizationService);
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
logService: LogService,
|
||||||
|
organizationService: OrganizationService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
collectionService,
|
||||||
|
platformUtilsService,
|
||||||
|
i18nService,
|
||||||
|
cipherService,
|
||||||
|
logService,
|
||||||
|
organizationService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,63 @@
|
|||||||
<div id="vault" class="vault" attr.aria-hidden="{{ showingModal }}">
|
<div id="vault" class="vault" attr.aria-hidden="{{ showingModal }}">
|
||||||
<app-vault-groupings id="groupings" class="groupings" (onAllClicked)="clearGroupingFilters()" (onFavoritesClicked)="filterFavorites()"
|
<app-vault-groupings
|
||||||
(onCipherTypeClicked)="filterCipherType($event)" (onFolderClicked)="filterFolder($event.id)"
|
id="groupings"
|
||||||
(onAddFolder)="addFolder()" (onEditFolder)="editFolder($event.id)"
|
class="groupings"
|
||||||
(onCollectionClicked)="filterCollection($event.id)" (onTrashClicked)="filterDeleted()">
|
(onAllClicked)="clearGroupingFilters()"
|
||||||
|
(onFavoritesClicked)="filterFavorites()"
|
||||||
|
(onCipherTypeClicked)="filterCipherType($event)"
|
||||||
|
(onFolderClicked)="filterFolder($event.id)"
|
||||||
|
(onAddFolder)="addFolder()"
|
||||||
|
(onEditFolder)="editFolder($event.id)"
|
||||||
|
(onCollectionClicked)="filterCollection($event.id)"
|
||||||
|
(onTrashClicked)="filterDeleted()"
|
||||||
|
>
|
||||||
</app-vault-groupings>
|
</app-vault-groupings>
|
||||||
<app-vault-ciphers id="items" class="items" [activeCipherId]="cipherId" (onCipherClicked)="viewCipher($event)"
|
<app-vault-ciphers
|
||||||
(onCipherRightClicked)="viewCipherMenu($event)" (onAddCipher)="addCipher($event)"
|
id="items"
|
||||||
(onAddCipherOptions)="addCipherOptions()">
|
class="items"
|
||||||
|
[activeCipherId]="cipherId"
|
||||||
|
(onCipherClicked)="viewCipher($event)"
|
||||||
|
(onCipherRightClicked)="viewCipherMenu($event)"
|
||||||
|
(onAddCipher)="addCipher($event)"
|
||||||
|
(onAddCipherOptions)="addCipherOptions()"
|
||||||
|
>
|
||||||
</app-vault-ciphers>
|
</app-vault-ciphers>
|
||||||
<app-vault-view id="details" class="details" *ngIf="cipherId && action === 'view'" [cipherId]="cipherId"
|
<app-vault-view
|
||||||
(onCloneCipher)="cloneCipherWithoutPasswordPrompt($event)" (onEditCipher)="editCipherWithoutPasswordPrompt($event)"
|
id="details"
|
||||||
(onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)" (onRestoredCipher)="restoredCipher($event)"
|
class="details"
|
||||||
(onDeletedCipher)="deletedCipher($event)">
|
*ngIf="cipherId && action === 'view'"
|
||||||
|
[cipherId]="cipherId"
|
||||||
|
(onCloneCipher)="cloneCipherWithoutPasswordPrompt($event)"
|
||||||
|
(onEditCipher)="editCipherWithoutPasswordPrompt($event)"
|
||||||
|
(onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)"
|
||||||
|
(onRestoredCipher)="restoredCipher($event)"
|
||||||
|
(onDeletedCipher)="deletedCipher($event)"
|
||||||
|
>
|
||||||
</app-vault-view>
|
</app-vault-view>
|
||||||
<app-vault-add-edit id="addEdit" class="details" *ngIf="action === 'add' || action === 'edit' || action === 'clone'"
|
<app-vault-add-edit
|
||||||
|
id="addEdit"
|
||||||
|
class="details"
|
||||||
|
*ngIf="action === 'add' || action === 'edit' || action === 'clone'"
|
||||||
[cloneMode]="action === 'clone'"
|
[cloneMode]="action === 'clone'"
|
||||||
[folderId]="action === 'add' && folderId !== 'none' ? folderId : null"
|
[folderId]="action === 'add' && folderId !== 'none' ? folderId : null"
|
||||||
[organizationId]="action === 'add' ? addOrganizationId : null"
|
[organizationId]="action === 'add' ? addOrganizationId : null"
|
||||||
[collectionIds]="action === 'add' ? addCollectionIds : null"
|
[collectionIds]="action === 'add' ? addCollectionIds : null"
|
||||||
[type]="action === 'add' ? (addType ? addType : type) : null" [cipherId]="(action === 'edit' || action === 'clone') ? cipherId : null"
|
[type]="action === 'add' ? (addType ? addType : type) : null"
|
||||||
(onSavedCipher)="savedCipher($event)" (onDeletedCipher)="deletedCipher($event)"
|
[cipherId]="action === 'edit' || action === 'clone' ? cipherId : null"
|
||||||
(onEditAttachments)="editCipherAttachments($event)" (onCancelled)="cancelledAddEdit($event)"
|
(onSavedCipher)="savedCipher($event)"
|
||||||
(onShareCipher)="shareCipher($event)" (onEditCollections)="cipherCollections($event)"
|
(onDeletedCipher)="deletedCipher($event)"
|
||||||
(onGeneratePassword)="openPasswordGenerator(true)">
|
(onEditAttachments)="editCipherAttachments($event)"
|
||||||
|
(onCancelled)="cancelledAddEdit($event)"
|
||||||
|
(onShareCipher)="shareCipher($event)"
|
||||||
|
(onEditCollections)="cipherCollections($event)"
|
||||||
|
(onGeneratePassword)="openPasswordGenerator(true)"
|
||||||
|
>
|
||||||
</app-vault-add-edit>
|
</app-vault-add-edit>
|
||||||
<div id="logo" class="logo" *ngIf="action !== 'add' && action !== 'edit' && action !== 'view' && action !== 'clone'">
|
<div
|
||||||
|
id="logo"
|
||||||
|
class="logo"
|
||||||
|
*ngIf="action !== 'add' && action !== 'edit' && action !== 'view' && action !== 'clone'"
|
||||||
|
>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="inner-content">
|
<div class="inner-content">
|
||||||
<img class="logo-image" alt="Bitwarden" aria-hidden="true" />
|
<img class="logo-image" alt="Bitwarden" aria-hidden="true" />
|
||||||
|
@ -6,66 +6,68 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
} from '@angular/core';
|
} from "@angular/core";
|
||||||
import {
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
ActivatedRoute,
|
|
||||||
Router,
|
|
||||||
} from '@angular/router';
|
|
||||||
|
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { SearchBarService } from '../layout/search/search-bar.service';
|
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||||
import { AddEditComponent } from './add-edit.component';
|
import { AddEditComponent } from "./add-edit.component";
|
||||||
import { AttachmentsComponent } from './attachments.component';
|
import { AttachmentsComponent } from "./attachments.component";
|
||||||
import { CiphersComponent } from './ciphers.component';
|
import { CiphersComponent } from "./ciphers.component";
|
||||||
import { CollectionsComponent } from './collections.component';
|
import { CollectionsComponent } from "./collections.component";
|
||||||
import { FolderAddEditComponent } from './folder-add-edit.component';
|
import { FolderAddEditComponent } from "./folder-add-edit.component";
|
||||||
import { GroupingsComponent } from './groupings.component';
|
import { GroupingsComponent } from "./groupings.component";
|
||||||
import { PasswordGeneratorComponent } from './password-generator.component';
|
import { PasswordGeneratorComponent } from "./password-generator.component";
|
||||||
import { PasswordHistoryComponent } from './password-history.component';
|
import { PasswordHistoryComponent } from "./password-history.component";
|
||||||
import { ShareComponent } from './share.component';
|
import { ShareComponent } from "./share.component";
|
||||||
import { ViewComponent } from './view.component';
|
import { ViewComponent } from "./view.component";
|
||||||
|
|
||||||
import { CipherRepromptType } from 'jslib-common/enums/cipherRepromptType';
|
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
|
||||||
import { CipherType } from 'jslib-common/enums/cipherType';
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
import { EventType } from 'jslib-common/enums/eventType';
|
import { EventType } from "jslib-common/enums/eventType";
|
||||||
|
|
||||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
import { FolderView } from 'jslib-common/models/view/folderView';
|
import { FolderView } from "jslib-common/models/view/folderView";
|
||||||
|
|
||||||
import { ModalRef } from 'jslib-angular/components/modal/modal.ref';
|
import { ModalRef } from "jslib-angular/components/modal/modal.ref";
|
||||||
|
|
||||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
|
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||||
|
|
||||||
import { invokeMenu, RendererMenuItem } from 'jslib-electron/utils';
|
import { invokeMenu, RendererMenuItem } from "jslib-electron/utils";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'VaultComponent';
|
const BroadcasterSubscriptionId = "VaultComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault',
|
selector: "app-vault",
|
||||||
templateUrl: 'vault.component.html',
|
templateUrl: "vault.component.html",
|
||||||
})
|
})
|
||||||
export class VaultComponent implements OnInit, OnDestroy {
|
export class VaultComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild(ViewComponent) viewComponent: ViewComponent;
|
@ViewChild(ViewComponent) viewComponent: ViewComponent;
|
||||||
@ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
|
@ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
|
||||||
@ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent;
|
@ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent;
|
||||||
@ViewChild(GroupingsComponent, { static: true }) groupingsComponent: GroupingsComponent;
|
@ViewChild(GroupingsComponent, { static: true }) groupingsComponent: GroupingsComponent;
|
||||||
@ViewChild('passwordGenerator', { read: ViewContainerRef, static: true }) passwordGeneratorModalRef: ViewContainerRef;
|
@ViewChild("passwordGenerator", { read: ViewContainerRef, static: true })
|
||||||
@ViewChild('attachments', { read: ViewContainerRef, static: true }) attachmentsModalRef: ViewContainerRef;
|
passwordGeneratorModalRef: ViewContainerRef;
|
||||||
@ViewChild('passwordHistory', { read: ViewContainerRef, static: true }) passwordHistoryModalRef: ViewContainerRef;
|
@ViewChild("attachments", { read: ViewContainerRef, static: true })
|
||||||
@ViewChild('share', { read: ViewContainerRef, static: true }) shareModalRef: ViewContainerRef;
|
attachmentsModalRef: ViewContainerRef;
|
||||||
@ViewChild('collections', { read: ViewContainerRef, static: true }) collectionsModalRef: ViewContainerRef;
|
@ViewChild("passwordHistory", { read: ViewContainerRef, static: true })
|
||||||
@ViewChild('folderAddEdit', { read: ViewContainerRef, static: true }) folderAddEditModalRef: ViewContainerRef;
|
passwordHistoryModalRef: ViewContainerRef;
|
||||||
|
@ViewChild("share", { read: ViewContainerRef, static: true }) shareModalRef: ViewContainerRef;
|
||||||
|
@ViewChild("collections", { read: ViewContainerRef, static: true })
|
||||||
|
collectionsModalRef: ViewContainerRef;
|
||||||
|
@ViewChild("folderAddEdit", { read: ViewContainerRef, static: true })
|
||||||
|
folderAddEditModalRef: ViewContainerRef;
|
||||||
|
|
||||||
action: string;
|
action: string;
|
||||||
cipherId: string = null;
|
cipherId: string = null;
|
||||||
@ -82,14 +84,23 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private modal: ModalRef = null;
|
private modal: ModalRef = null;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router,
|
constructor(
|
||||||
private i18nService: I18nService, private modalService: ModalService,
|
private route: ActivatedRoute,
|
||||||
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef,
|
private router: Router,
|
||||||
private ngZone: NgZone, private syncService: SyncService,
|
private i18nService: I18nService,
|
||||||
|
private modalService: ModalService,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private ngZone: NgZone,
|
||||||
|
private syncService: SyncService,
|
||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private platformUtilsService: PlatformUtilsService, private eventService: EventService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private totpService: TotpService, private passwordRepromptService: PasswordRepromptService,
|
private eventService: EventService,
|
||||||
private stateService: StateService, private searchBarService: SearchBarService) { }
|
private totpService: TotpService,
|
||||||
|
private passwordRepromptService: PasswordRepromptService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private searchBarService: SearchBarService
|
||||||
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
|
this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
|
||||||
@ -98,60 +109,80 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
let detectChanges = true;
|
let detectChanges = true;
|
||||||
|
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'newLogin':
|
case "newLogin":
|
||||||
await this.addCipher(CipherType.Login);
|
await this.addCipher(CipherType.Login);
|
||||||
break;
|
break;
|
||||||
case 'newCard':
|
case "newCard":
|
||||||
await this.addCipher(CipherType.Card);
|
await this.addCipher(CipherType.Card);
|
||||||
break;
|
break;
|
||||||
case 'newIdentity':
|
case "newIdentity":
|
||||||
await this.addCipher(CipherType.Identity);
|
await this.addCipher(CipherType.Identity);
|
||||||
break;
|
break;
|
||||||
case 'newSecureNote':
|
case "newSecureNote":
|
||||||
await this.addCipher(CipherType.SecureNote);
|
await this.addCipher(CipherType.SecureNote);
|
||||||
break;
|
break;
|
||||||
case 'focusSearch':
|
case "focusSearch":
|
||||||
(document.querySelector('#search') as HTMLInputElement).select();
|
(document.querySelector("#search") as HTMLInputElement).select();
|
||||||
detectChanges = false;
|
detectChanges = false;
|
||||||
break;
|
break;
|
||||||
case 'openPasswordGenerator':
|
case "openPasswordGenerator":
|
||||||
await this.openPasswordGenerator(false);
|
await this.openPasswordGenerator(false);
|
||||||
break;
|
break;
|
||||||
case 'syncCompleted':
|
case "syncCompleted":
|
||||||
await this.load();
|
await this.load();
|
||||||
break;
|
break;
|
||||||
case 'refreshCiphers':
|
case "refreshCiphers":
|
||||||
this.ciphersComponent.refresh();
|
this.ciphersComponent.refresh();
|
||||||
break;
|
break;
|
||||||
case 'modalShown':
|
case "modalShown":
|
||||||
this.showingModal = true;
|
this.showingModal = true;
|
||||||
break;
|
break;
|
||||||
case 'modalClosed':
|
case "modalClosed":
|
||||||
this.showingModal = false;
|
this.showingModal = false;
|
||||||
break;
|
break;
|
||||||
case 'copyUsername':
|
case "copyUsername":
|
||||||
const uComponent = this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
|
const uComponent =
|
||||||
|
this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
|
||||||
const uCipher = uComponent != null ? uComponent.cipher : null;
|
const uCipher = uComponent != null ? uComponent.cipher : null;
|
||||||
if (this.cipherId != null && uCipher != null && uCipher.id === this.cipherId &&
|
if (
|
||||||
uCipher.login != null && uCipher.login.username != null) {
|
this.cipherId != null &&
|
||||||
this.copyValue(uCipher, uCipher.login.username, 'username', 'Username');
|
uCipher != null &&
|
||||||
|
uCipher.id === this.cipherId &&
|
||||||
|
uCipher.login != null &&
|
||||||
|
uCipher.login.username != null
|
||||||
|
) {
|
||||||
|
this.copyValue(uCipher, uCipher.login.username, "username", "Username");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'copyPassword':
|
case "copyPassword":
|
||||||
const pComponent = this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
|
const pComponent =
|
||||||
|
this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
|
||||||
const pCipher = pComponent != null ? pComponent.cipher : null;
|
const pCipher = pComponent != null ? pComponent.cipher : null;
|
||||||
if (this.cipherId != null && pCipher != null && pCipher.id === this.cipherId &&
|
if (
|
||||||
pCipher.login != null && pCipher.login.password != null && pCipher.viewPassword) {
|
this.cipherId != null &&
|
||||||
this.copyValue(pCipher, pCipher.login.password, 'password', 'Password');
|
pCipher != null &&
|
||||||
|
pCipher.id === this.cipherId &&
|
||||||
|
pCipher.login != null &&
|
||||||
|
pCipher.login.password != null &&
|
||||||
|
pCipher.viewPassword
|
||||||
|
) {
|
||||||
|
this.copyValue(pCipher, pCipher.login.password, "password", "Password");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'copyTotp':
|
case "copyTotp":
|
||||||
const tComponent = this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
|
const tComponent =
|
||||||
|
this.addEditComponent == null ? this.viewComponent : this.addEditComponent;
|
||||||
const tCipher = tComponent != null ? tComponent.cipher : null;
|
const tCipher = tComponent != null ? tComponent.cipher : null;
|
||||||
if (this.cipherId != null && tCipher != null && tCipher.id === this.cipherId &&
|
if (
|
||||||
tCipher.login != null && tCipher.login.hasTotp && this.userHasPremiumAccess) {
|
this.cipherId != null &&
|
||||||
|
tCipher != null &&
|
||||||
|
tCipher.id === this.cipherId &&
|
||||||
|
tCipher.login != null &&
|
||||||
|
tCipher.login.hasTotp &&
|
||||||
|
this.userHasPremiumAccess
|
||||||
|
) {
|
||||||
const value = await this.totpService.getCode(tCipher.login.totp);
|
const value = await this.totpService.getCode(tCipher.login.totp);
|
||||||
this.copyValue(tCipher, value, 'verificationCodeTotp', 'TOTP');
|
this.copyValue(tCipher, value, "verificationCodeTotp", "TOTP");
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
detectChanges = false;
|
detectChanges = false;
|
||||||
@ -167,20 +198,20 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
if (!this.syncService.syncInProgress) {
|
if (!this.syncService.syncInProgress) {
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
document.body.classList.remove('layout_frontend');
|
document.body.classList.remove("layout_frontend");
|
||||||
|
|
||||||
this.searchBarService.setEnabled(true);
|
this.searchBarService.setEnabled(true);
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchVault'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.searchBarService.setEnabled(false);
|
this.searchBarService.setEnabled(false);
|
||||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||||
document.body.classList.add('layout_frontend');
|
document.body.classList.add("layout_frontend");
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.route.queryParams.pipe(first()).subscribe(async params => {
|
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||||
await this.groupingsComponent.load();
|
await this.groupingsComponent.load();
|
||||||
|
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
@ -190,14 +221,14 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
if (params.cipherId) {
|
if (params.cipherId) {
|
||||||
const cipherView = new CipherView();
|
const cipherView = new CipherView();
|
||||||
cipherView.id = params.cipherId;
|
cipherView.id = params.cipherId;
|
||||||
if (params.action === 'clone') {
|
if (params.action === "clone") {
|
||||||
await this.cloneCipher(cipherView);
|
await this.cloneCipher(cipherView);
|
||||||
} else if (params.action === 'edit') {
|
} else if (params.action === "edit") {
|
||||||
await this.editCipher(cipherView);
|
await this.editCipher(cipherView);
|
||||||
} else {
|
} else {
|
||||||
await this.viewCipher(cipherView);
|
await this.viewCipher(cipherView);
|
||||||
}
|
}
|
||||||
} else if (params.action === 'add') {
|
} else if (params.action === "add") {
|
||||||
this.addType = Number(params.addType);
|
this.addType = Number(params.addType);
|
||||||
this.addCipher(this.addType);
|
this.addCipher(this.addType);
|
||||||
}
|
}
|
||||||
@ -208,7 +239,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
} else if (params.favorites) {
|
} else if (params.favorites) {
|
||||||
this.groupingsComponent.selectedFavorites = true;
|
this.groupingsComponent.selectedFavorites = true;
|
||||||
await this.filterFavorites();
|
await this.filterFavorites();
|
||||||
} else if (params.type && params.action !== 'add') {
|
} else if (params.type && params.action !== "add") {
|
||||||
const t = parseInt(params.type, null);
|
const t = parseInt(params.type, null);
|
||||||
this.groupingsComponent.selectedType = t;
|
this.groupingsComponent.selectedType = t;
|
||||||
await this.filterCipherType(t);
|
await this.filterCipherType(t);
|
||||||
@ -228,34 +259,37 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async viewCipher(cipher: CipherView) {
|
async viewCipher(cipher: CipherView) {
|
||||||
if (!await this.canNavigateAway('view', cipher)) {
|
if (!(await this.canNavigateAway("view", cipher))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cipherId = cipher.id;
|
this.cipherId = cipher.id;
|
||||||
this.action = 'view';
|
this.action = "view";
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
viewCipherMenu(cipher: CipherView) {
|
viewCipherMenu(cipher: CipherView) {
|
||||||
const menu: RendererMenuItem[] = [
|
const menu: RendererMenuItem[] = [
|
||||||
{
|
{
|
||||||
label: this.i18nService.t('view'),
|
label: this.i18nService.t("view"),
|
||||||
click: () => this.functionWithChangeDetection(() => {
|
click: () =>
|
||||||
|
this.functionWithChangeDetection(() => {
|
||||||
this.viewCipher(cipher);
|
this.viewCipher(cipher);
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (!cipher.isDeleted) {
|
if (!cipher.isDeleted) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('edit'),
|
label: this.i18nService.t("edit"),
|
||||||
click: () => this.functionWithChangeDetection(() => {
|
click: () =>
|
||||||
|
this.functionWithChangeDetection(() => {
|
||||||
this.editCipher(cipher);
|
this.editCipher(cipher);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('clone'),
|
label: this.i18nService.t("clone"),
|
||||||
click: () => this.functionWithChangeDetection(() => {
|
click: () =>
|
||||||
|
this.functionWithChangeDetection(() => {
|
||||||
this.cloneCipher(cipher);
|
this.cloneCipher(cipher);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@ -263,55 +297,59 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
switch (cipher.type) {
|
switch (cipher.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
if (cipher.login.canLaunch || cipher.login.username != null || cipher.login.password != null) {
|
if (
|
||||||
menu.push({ type: 'separator' });
|
cipher.login.canLaunch ||
|
||||||
|
cipher.login.username != null ||
|
||||||
|
cipher.login.password != null
|
||||||
|
) {
|
||||||
|
menu.push({ type: "separator" });
|
||||||
}
|
}
|
||||||
if (cipher.login.canLaunch) {
|
if (cipher.login.canLaunch) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('launch'),
|
label: this.i18nService.t("launch"),
|
||||||
click: () => this.platformUtilsService.launchUri(cipher.login.launchUri),
|
click: () => this.platformUtilsService.launchUri(cipher.login.launchUri),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cipher.login.username != null) {
|
if (cipher.login.username != null) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('copyUsername'),
|
label: this.i18nService.t("copyUsername"),
|
||||||
click: () => this.copyValue(cipher, cipher.login.username, 'username', 'Username'),
|
click: () => this.copyValue(cipher, cipher.login.username, "username", "Username"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cipher.login.password != null && cipher.viewPassword) {
|
if (cipher.login.password != null && cipher.viewPassword) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('copyPassword'),
|
label: this.i18nService.t("copyPassword"),
|
||||||
click: () => {
|
click: () => {
|
||||||
this.copyValue(cipher, cipher.login.password, 'password', 'Password');
|
this.copyValue(cipher, cipher.login.password, "password", "Password");
|
||||||
this.eventService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
|
this.eventService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) {
|
if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('copyVerificationCodeTotp'),
|
label: this.i18nService.t("copyVerificationCodeTotp"),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const value = await this.totpService.getCode(cipher.login.totp);
|
const value = await this.totpService.getCode(cipher.login.totp);
|
||||||
this.copyValue(cipher, value, 'verificationCodeTotp', 'TOTP');
|
this.copyValue(cipher, value, "verificationCodeTotp", "TOTP");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
if (cipher.card.number != null || cipher.card.code != null) {
|
if (cipher.card.number != null || cipher.card.code != null) {
|
||||||
menu.push({ type: 'separator' });
|
menu.push({ type: "separator" });
|
||||||
}
|
}
|
||||||
if (cipher.card.number != null) {
|
if (cipher.card.number != null) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('copyNumber'),
|
label: this.i18nService.t("copyNumber"),
|
||||||
click: () => this.copyValue(cipher, cipher.card.number, 'number', 'Card Number'),
|
click: () => this.copyValue(cipher, cipher.card.number, "number", "Card Number"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cipher.card.code != null) {
|
if (cipher.card.code != null) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.i18nService.t('copySecurityCode'),
|
label: this.i18nService.t("copySecurityCode"),
|
||||||
click: () => {
|
click: () => {
|
||||||
this.copyValue(cipher, cipher.card.code, 'securityCode', 'Security Code');
|
this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code");
|
||||||
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -325,9 +363,9 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async editCipher(cipher: CipherView) {
|
async editCipher(cipher: CipherView) {
|
||||||
if (!await this.canNavigateAway('edit', cipher)) {
|
if (!(await this.canNavigateAway("edit", cipher))) {
|
||||||
return;
|
return;
|
||||||
} else if (!await this.passwordReprompt(cipher)) {
|
} else if (!(await this.passwordReprompt(cipher))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,19 +373,19 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async editCipherWithoutPasswordPrompt(cipher: CipherView) {
|
async editCipherWithoutPasswordPrompt(cipher: CipherView) {
|
||||||
if (!await this.canNavigateAway('edit', cipher)) {
|
if (!(await this.canNavigateAway("edit", cipher))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cipherId = cipher.id;
|
this.cipherId = cipher.id;
|
||||||
this.action = 'edit';
|
this.action = "edit";
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async cloneCipher(cipher: CipherView) {
|
async cloneCipher(cipher: CipherView) {
|
||||||
if (!await this.canNavigateAway('clone', cipher)) {
|
if (!(await this.canNavigateAway("clone", cipher))) {
|
||||||
return;
|
return;
|
||||||
} else if (!await this.passwordReprompt(cipher)) {
|
} else if (!(await this.passwordReprompt(cipher))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,22 +393,22 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async cloneCipherWithoutPasswordPrompt(cipher: CipherView) {
|
async cloneCipherWithoutPasswordPrompt(cipher: CipherView) {
|
||||||
if (!await this.canNavigateAway('edit', cipher)) {
|
if (!(await this.canNavigateAway("edit", cipher))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cipherId = cipher.id;
|
this.cipherId = cipher.id;
|
||||||
this.action = 'clone';
|
this.action = "clone";
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addCipher(type: CipherType = null) {
|
async addCipher(type: CipherType = null) {
|
||||||
if (!await this.canNavigateAway('add', null)) {
|
if (!(await this.canNavigateAway("add", null))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addType = type;
|
this.addType = type;
|
||||||
this.action = 'add';
|
this.action = "add";
|
||||||
this.cipherId = null;
|
this.cipherId = null;
|
||||||
this.updateCollectionProperties();
|
this.updateCollectionProperties();
|
||||||
this.go();
|
this.go();
|
||||||
@ -379,22 +417,21 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
addCipherOptions() {
|
addCipherOptions() {
|
||||||
const menu: RendererMenuItem[] = [
|
const menu: RendererMenuItem[] = [
|
||||||
{
|
{
|
||||||
label: this.i18nService.t('typeLogin'),
|
label: this.i18nService.t("typeLogin"),
|
||||||
click: () => this.addCipherWithChangeDetection(CipherType.Login),
|
click: () => this.addCipherWithChangeDetection(CipherType.Login),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.i18nService.t('typeCard'),
|
label: this.i18nService.t("typeCard"),
|
||||||
click: () => this.addCipherWithChangeDetection(CipherType.Card),
|
click: () => this.addCipherWithChangeDetection(CipherType.Card),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.i18nService.t('typeIdentity'),
|
label: this.i18nService.t("typeIdentity"),
|
||||||
click: () => this.addCipherWithChangeDetection(CipherType.Identity),
|
click: () => this.addCipherWithChangeDetection(CipherType.Identity),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.i18nService.t('typeSecureNote'),
|
label: this.i18nService.t("typeSecureNote"),
|
||||||
click: () => this.addCipherWithChangeDetection(CipherType.SecureNote),
|
click: () => this.addCipherWithChangeDetection(CipherType.SecureNote),
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
invokeMenu(menu);
|
invokeMenu(menu);
|
||||||
@ -402,7 +439,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async savedCipher(cipher: CipherView) {
|
async savedCipher(cipher: CipherView) {
|
||||||
this.cipherId = cipher.id;
|
this.cipherId = cipher.id;
|
||||||
this.action = 'view';
|
this.action = "view";
|
||||||
this.go();
|
this.go();
|
||||||
await this.ciphersComponent.refresh();
|
await this.ciphersComponent.refresh();
|
||||||
}
|
}
|
||||||
@ -426,13 +463,16 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(AttachmentsComponent, this.attachmentsModalRef,
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
comp => comp.cipherId = cipher.id);
|
AttachmentsComponent,
|
||||||
|
this.attachmentsModalRef,
|
||||||
|
(comp) => (comp.cipherId = cipher.id)
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
let madeAttachmentChanges = false;
|
let madeAttachmentChanges = false;
|
||||||
childComponent.onUploadedAttachment.subscribe(() => madeAttachmentChanges = true);
|
childComponent.onUploadedAttachment.subscribe(() => (madeAttachmentChanges = true));
|
||||||
childComponent.onDeletedAttachment.subscribe(() => madeAttachmentChanges = true);
|
childComponent.onDeletedAttachment.subscribe(() => (madeAttachmentChanges = true));
|
||||||
|
|
||||||
this.modal.onClosed.subscribe(async () => {
|
this.modal.onClosed.subscribe(async () => {
|
||||||
this.modal = null;
|
this.modal = null;
|
||||||
@ -448,8 +488,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(ShareComponent, this.shareModalRef,
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
comp => comp.cipherId = cipher.id);
|
ShareComponent,
|
||||||
|
this.shareModalRef,
|
||||||
|
(comp) => (comp.cipherId = cipher.id)
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSharedCipher.subscribe(async () => {
|
childComponent.onSharedCipher.subscribe(async () => {
|
||||||
@ -467,8 +510,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(CollectionsComponent, this.collectionsModalRef,
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
comp => comp.cipherId = cipher.id);
|
CollectionsComponent,
|
||||||
|
this.collectionsModalRef,
|
||||||
|
(comp) => (comp.cipherId = cipher.id)
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSavedCollections.subscribe(() => {
|
childComponent.onSavedCollections.subscribe(() => {
|
||||||
@ -485,8 +531,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
[this.modal] = await this.modalService.openViewRef(PasswordHistoryComponent, this.passwordHistoryModalRef,
|
[this.modal] = await this.modalService.openViewRef(
|
||||||
comp => comp.cipherId = cipher.id);
|
PasswordHistoryComponent,
|
||||||
|
this.passwordHistoryModalRef,
|
||||||
|
(comp) => (comp.cipherId = cipher.id)
|
||||||
|
);
|
||||||
|
|
||||||
this.modal.onClosed.subscribe(async () => {
|
this.modal.onClosed.subscribe(async () => {
|
||||||
this.modal = null;
|
this.modal = null;
|
||||||
@ -495,27 +544,27 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
cancelledAddEdit(cipher: CipherView) {
|
cancelledAddEdit(cipher: CipherView) {
|
||||||
this.cipherId = cipher.id;
|
this.cipherId = cipher.id;
|
||||||
this.action = this.cipherId != null ? 'view' : null;
|
this.action = this.cipherId != null ? "view" : null;
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async clearGroupingFilters() {
|
async clearGroupingFilters() {
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchVault'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
|
||||||
await this.ciphersComponent.reload();
|
await this.ciphersComponent.reload();
|
||||||
this.clearFilters();
|
this.clearFilters();
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterFavorites() {
|
async filterFavorites() {
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchFavorites'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchFavorites"));
|
||||||
await this.ciphersComponent.reload(c => c.favorite);
|
await this.ciphersComponent.reload((c) => c.favorite);
|
||||||
this.clearFilters();
|
this.clearFilters();
|
||||||
this.favorites = true;
|
this.favorites = true;
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterDeleted() {
|
async filterDeleted() {
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchTrash'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchTrash"));
|
||||||
this.ciphersComponent.deleted = true;
|
this.ciphersComponent.deleted = true;
|
||||||
await this.ciphersComponent.reload(null, true);
|
await this.ciphersComponent.reload(null, true);
|
||||||
this.clearFilters();
|
this.clearFilters();
|
||||||
@ -524,26 +573,27 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async filterCipherType(type: CipherType) {
|
async filterCipherType(type: CipherType) {
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchType'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchType"));
|
||||||
await this.ciphersComponent.reload(c => c.type === type);
|
await this.ciphersComponent.reload((c) => c.type === type);
|
||||||
this.clearFilters();
|
this.clearFilters();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterFolder(folderId: string) {
|
async filterFolder(folderId: string) {
|
||||||
folderId = folderId === 'none' ? null : folderId;
|
folderId = folderId === "none" ? null : folderId;
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchFolder'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchFolder"));
|
||||||
await this.ciphersComponent.reload(c => c.folderId === folderId);
|
await this.ciphersComponent.reload((c) => c.folderId === folderId);
|
||||||
this.clearFilters();
|
this.clearFilters();
|
||||||
this.folderId = folderId == null ? 'none' : folderId;
|
this.folderId = folderId == null ? "none" : folderId;
|
||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterCollection(collectionId: string) {
|
async filterCollection(collectionId: string) {
|
||||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchCollection'));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchCollection"));
|
||||||
await this.ciphersComponent.reload(c => c.collectionIds != null &&
|
await this.ciphersComponent.reload(
|
||||||
c.collectionIds.indexOf(collectionId) > -1);
|
(c) => c.collectionIds != null && c.collectionIds.indexOf(collectionId) > -1
|
||||||
|
);
|
||||||
this.clearFilters();
|
this.clearFilters();
|
||||||
this.collectionId = collectionId;
|
this.collectionId = collectionId;
|
||||||
this.updateCollectionProperties();
|
this.updateCollectionProperties();
|
||||||
@ -555,14 +605,21 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(PasswordGeneratorComponent, this.passwordGeneratorModalRef,
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
comp => comp.showSelect = showSelect);
|
PasswordGeneratorComponent,
|
||||||
|
this.passwordGeneratorModalRef,
|
||||||
|
(comp) => (comp.showSelect = showSelect)
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSelected.subscribe((password: string) => {
|
childComponent.onSelected.subscribe((password: string) => {
|
||||||
this.modal.close();
|
this.modal.close();
|
||||||
if (this.addEditComponent != null && this.addEditComponent.cipher != null &&
|
if (
|
||||||
this.addEditComponent.cipher.type === CipherType.Login && this.addEditComponent.cipher.login != null) {
|
this.addEditComponent != null &&
|
||||||
|
this.addEditComponent.cipher != null &&
|
||||||
|
this.addEditComponent.cipher.type === CipherType.Login &&
|
||||||
|
this.addEditComponent.cipher.login != null
|
||||||
|
) {
|
||||||
this.addEditComponent.markPasswordAsDirty();
|
this.addEditComponent.markPasswordAsDirty();
|
||||||
this.addEditComponent.cipher.login.password = password;
|
this.addEditComponent.cipher.login.password = password;
|
||||||
}
|
}
|
||||||
@ -574,7 +631,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addFolder() {
|
async addFolder() {
|
||||||
this.messagingService.send('newFolder');
|
this.messagingService.send("newFolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
async editFolder(folderId: string) {
|
async editFolder(folderId: string) {
|
||||||
@ -582,8 +639,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(FolderAddEditComponent, this.folderAddEditModalRef,
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
comp => comp.folderId = folderId);
|
FolderAddEditComponent,
|
||||||
|
this.folderAddEditModalRef,
|
||||||
|
(comp) => (comp.folderId = folderId)
|
||||||
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSavedFolder.subscribe(async (folder: FolderView) => {
|
childComponent.onSavedFolder.subscribe(async (folder: FolderView) => {
|
||||||
@ -601,14 +661,20 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private dirtyInput(): boolean {
|
private dirtyInput(): boolean {
|
||||||
return (this.action === 'add' || this.action === 'edit' || this.action === 'clone') &&
|
return (
|
||||||
document.querySelectorAll('app-vault-add-edit .ng-dirty').length > 0;
|
(this.action === "add" || this.action === "edit" || this.action === "clone") &&
|
||||||
|
document.querySelectorAll("app-vault-add-edit .ng-dirty").length > 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async wantsToSaveChanges(): Promise<boolean> {
|
private async wantsToSaveChanges(): Promise<boolean> {
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('unsavedChangesConfirmation'), this.i18nService.t('unsavedChangesTitle'),
|
this.i18nService.t("unsavedChangesConfirmation"),
|
||||||
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
this.i18nService.t("unsavedChangesTitle"),
|
||||||
|
this.i18nService.t("yes"),
|
||||||
|
this.i18nService.t("no"),
|
||||||
|
"warning"
|
||||||
|
);
|
||||||
return !confirmed;
|
return !confirmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,16 +715,22 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) {
|
private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) {
|
||||||
this.functionWithChangeDetection(async () => {
|
this.functionWithChangeDetection(async () => {
|
||||||
if (cipher.reprompt !== CipherRepromptType.None && this.passwordRepromptService.protectedFields().includes(aType) &&
|
if (
|
||||||
!await this.passwordRepromptService.showPasswordPrompt()) {
|
cipher.reprompt !== CipherRepromptType.None &&
|
||||||
|
this.passwordRepromptService.protectedFields().includes(aType) &&
|
||||||
|
!(await this.passwordRepromptService.showPasswordPrompt())
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.platformUtilsService.copyToClipboard(value);
|
this.platformUtilsService.copyToClipboard(value);
|
||||||
this.platformUtilsService.showToast('info', null,
|
this.platformUtilsService.showToast(
|
||||||
this.i18nService.t('valueCopied', this.i18nService.t(labelI18nKey)));
|
"info",
|
||||||
if (this.action === 'view') {
|
null,
|
||||||
this.messagingService.send('minimizeOnCopy');
|
this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey))
|
||||||
|
);
|
||||||
|
if (this.action === "view") {
|
||||||
|
this.messagingService.send("minimizeOnCopy");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -672,7 +744,9 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private updateCollectionProperties() {
|
private updateCollectionProperties() {
|
||||||
if (this.collectionId != null) {
|
if (this.collectionId != null) {
|
||||||
const collection = this.groupingsComponent.collections.filter(c => c.id === this.collectionId);
|
const collection = this.groupingsComponent.collections.filter(
|
||||||
|
(c) => c.id === this.collectionId
|
||||||
|
);
|
||||||
if (collection.length > 0) {
|
if (collection.length > 0) {
|
||||||
this.addOrganizationId = collection[0].organizationId;
|
this.addOrganizationId = collection[0].organizationId;
|
||||||
this.addCollectionIds = [this.collectionId];
|
this.addCollectionIds = [this.collectionId];
|
||||||
@ -687,7 +761,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
// Don't navigate to same route
|
// Don't navigate to same route
|
||||||
if (this.action === action && (cipher == null || this.cipherId === cipher.id)) {
|
if (this.action === action && (cipher == null || this.cipherId === cipher.id)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (this.dirtyInput() && await this.wantsToSaveChanges()) {
|
} else if (this.dirtyInput() && (await this.wantsToSaveChanges())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,6 +769,9 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async passwordReprompt(cipher: CipherView) {
|
private async passwordReprompt(cipher: CipherView) {
|
||||||
return cipher.reprompt === CipherRepromptType.None || await this.passwordRepromptService.showPasswordPrompt();
|
return (
|
||||||
|
cipher.reprompt === CipherRepromptType.None ||
|
||||||
|
(await this.passwordRepromptService.showPasswordPrompt())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'customFields' | i18n}}
|
{{ "customFields" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" *ngFor="let field of cipher.fields">
|
<div class="box-content-row box-content-row-flex" *ngFor="let field of cipher.fields">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label">{{ field.name }}</span>
|
<span class="row-label">{{ field.name }}</span>
|
||||||
<div *ngIf="field.type === fieldType.Text">
|
<div *ngIf="field.type === fieldType.Text">
|
||||||
{{field.value || ' '}}
|
{{ field.value || " " }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="field.type === fieldType.Hidden">
|
<div *ngIf="field.type === fieldType.Hidden">
|
||||||
<span *ngIf="field.showValue" class="monospaced show-whitespace">{{ field.value }}</span>
|
<span *ngIf="field.showValue" class="monospaced show-whitespace">{{ field.value }}</span>
|
||||||
@ -21,23 +21,43 @@
|
|||||||
<div *ngIf="field.type === fieldType.Linked" class="box-content-row-flex">
|
<div *ngIf="field.type === fieldType.Linked" class="box-content-row-flex">
|
||||||
<div class="icon icon-small">
|
<div class="icon icon-small">
|
||||||
<i class="fa fa-link" aria-hidden="true" appA11yTitle="{{ 'linkedValue' | i18n }}"></i>
|
<i class="fa fa-link" aria-hidden="true" appA11yTitle="{{ 'linkedValue' | i18n }}"></i>
|
||||||
<span class="sr-only">{{'linkedValue' | i18n}}</span>
|
<span class="sr-only">{{ "linkedValue" | i18n }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span>{{ cipher.linkedFieldI18nKey(field.linkedId) | i18n }}</span>
|
<span>{{ cipher.linkedFieldI18nKey(field.linkedId) | i18n }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
<a
|
||||||
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
*ngIf="field.type === fieldType.Hidden && cipher.viewPassword"
|
*ngIf="field.type === fieldType.Hidden && cipher.viewPassword"
|
||||||
(click)="toggleFieldValue(field)" role="button">
|
(click)="toggleFieldValue(field)"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
role="button"
|
||||||
[ngClass]="{'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue}"></i>
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyValue' | i18n}}"
|
<a
|
||||||
*ngIf="field.value && field.type !== fieldType.Boolean && field.type !== fieldType.Linked &&
|
class="row-btn"
|
||||||
!(field.type === fieldType.Hidden && !cipher.viewPassword)"
|
href="#"
|
||||||
(click)="copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')"
|
appStopClick
|
||||||
role="button">
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
|
*ngIf="
|
||||||
|
field.value &&
|
||||||
|
field.type !== fieldType.Boolean &&
|
||||||
|
field.type !== fieldType.Linked &&
|
||||||
|
!(field.type === fieldType.Hidden && !cipher.viewPassword)
|
||||||
|
"
|
||||||
|
(click)="
|
||||||
|
copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')
|
||||||
|
"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import {
|
import { Component } from "@angular/core";
|
||||||
Component,
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
|
|
||||||
import {
|
import { ViewCustomFieldsComponent as BaseViewCustomFieldsComponent } from "jslib-angular/components/view-custom-fields.component";
|
||||||
ViewCustomFieldsComponent as BaseViewCustomFieldsComponent
|
|
||||||
} from 'jslib-angular/components/view-custom-fields.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-view-custom-fields',
|
selector: "app-vault-view-custom-fields",
|
||||||
templateUrl: 'view-custom-fields.component.html',
|
templateUrl: "view-custom-fields.component.html",
|
||||||
})
|
})
|
||||||
export class ViewCustomFieldsComponent extends BaseViewCustomFieldsComponent {
|
export class ViewCustomFieldsComponent extends BaseViewCustomFieldsComponent {
|
||||||
constructor(eventService: EventService) {
|
constructor(eventService: EventService) {
|
||||||
|
@ -2,77 +2,142 @@
|
|||||||
<div class="inner-content" *ngIf="cipher">
|
<div class="inner-content" *ngIf="cipher">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'itemInformation' | i18n}}
|
{{ "itemInformation" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row">
|
<div class="box-content-row">
|
||||||
<span class="row-label">{{'name' | i18n}}</span>
|
<span class="row-label">{{ "name" | i18n }}</span>
|
||||||
{{ cipher.name }}
|
{{ cipher.name }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Login -->
|
<!-- Login -->
|
||||||
<div *ngIf="cipher.login">
|
<div *ngIf="cipher.login">
|
||||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.username">
|
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.username">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label draggable" draggable="true"
|
<span
|
||||||
(dragstart)="setTextDataOnDrag($event, cipher.login.username)">{{'username' | i18n}}</span>
|
class="row-label draggable"
|
||||||
|
draggable="true"
|
||||||
|
(dragstart)="setTextDataOnDrag($event, cipher.login.username)"
|
||||||
|
>{{ "username" | i18n }}</span
|
||||||
|
>
|
||||||
{{ cipher.login.username }}
|
{{ cipher.login.username }}
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyUsername' | i18n}}"
|
<a
|
||||||
(click)="copy(cipher.login.username, 'username', 'Username')" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||||
|
(click)="copy(cipher.login.username, 'username', 'Username')"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.password">
|
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.password">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label draggable" draggable="true"
|
<span
|
||||||
(dragstart)="setTextDataOnDrag($event, cipher.login.password)">{{'password' | i18n}}</span>
|
class="row-label draggable"
|
||||||
|
draggable="true"
|
||||||
|
(dragstart)="setTextDataOnDrag($event, cipher.login.password)"
|
||||||
|
>{{ "password" | i18n }}</span
|
||||||
|
>
|
||||||
<div *ngIf="!showPassword" class="monospaced">
|
<div *ngIf="!showPassword" class="monospaced">
|
||||||
{{cipher.login.maskedPassword}}</div>
|
{{ cipher.login.maskedPassword }}
|
||||||
<div *ngIf="showPassword" class="monospaced password-wrapper" appSelectCopy
|
|
||||||
[innerHTML]="cipher.login.password | colorPassword"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons" *ngIf=cipher.viewPassword>
|
<div
|
||||||
<button type="button" #checkPasswordBtn class="row-btn btn" appBlurClick
|
*ngIf="showPassword"
|
||||||
appA11yTitle="{{'checkPassword' | i18n}}" (click)="checkPassword()"
|
class="monospaced password-wrapper"
|
||||||
[appApiAction]="checkPasswordPromise" [disabled]="checkPasswordBtn.loading">
|
appSelectCopy
|
||||||
<i class="fa fa-lg fa-check-circle" [hidden]="checkPasswordBtn.loading"
|
[innerHTML]="cipher.login.password | colorPassword"
|
||||||
aria-hidden="true"></i>
|
></div>
|
||||||
<i class="fa fa-lg fa-spinner fa-spin" [hidden]="!checkPasswordBtn.loading"
|
</div>
|
||||||
aria-hidden="true"></i>
|
<div class="action-buttons" *ngIf="cipher.viewPassword">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
#checkPasswordBtn
|
||||||
|
class="row-btn btn"
|
||||||
|
appBlurClick
|
||||||
|
appA11yTitle="{{ 'checkPassword' | i18n }}"
|
||||||
|
(click)="checkPassword()"
|
||||||
|
[appApiAction]="checkPasswordPromise"
|
||||||
|
[disabled]="checkPasswordBtn.loading"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg fa-check-circle"
|
||||||
|
[hidden]="checkPasswordBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg fa-spinner fa-spin"
|
||||||
|
[hidden]="!checkPasswordBtn.loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
<a
|
||||||
(click)="togglePassword()" role="button">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="togglePassword()"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
<a
|
||||||
(click)="copy(cipher.login.password, 'password', 'Password')" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||||
|
(click)="copy(cipher.login.password, 'password', 'Password')"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex totp" [ngClass]="{'low': totpLow}"
|
<div
|
||||||
*ngIf="cipher.login.totp && totpCode">
|
class="box-content-row box-content-row-flex totp"
|
||||||
|
[ngClass]="{ low: totpLow }"
|
||||||
|
*ngIf="cipher.login.totp && totpCode"
|
||||||
|
>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label draggable" draggable="true"
|
<span
|
||||||
(dragstart)="setTextDataOnDrag($event, totpCode)">{{'verificationCodeTotp' | i18n}}</span>
|
class="row-label draggable"
|
||||||
|
draggable="true"
|
||||||
|
(dragstart)="setTextDataOnDrag($event, totpCode)"
|
||||||
|
>{{ "verificationCodeTotp" | i18n }}</span
|
||||||
|
>
|
||||||
<span class="totp-code">{{ totpCodeFormatted }}</span>
|
<span class="totp-code">{{ totpCodeFormatted }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="totp-countdown">
|
<span class="totp-countdown">
|
||||||
<span class="totp-sec">{{ totpSec }}</span>
|
<span class="totp-sec">{{ totpSec }}</span>
|
||||||
<svg>
|
<svg>
|
||||||
<g>
|
<g>
|
||||||
<circle class="totp-circle inner" r="12.6" cy="16" cx="16"
|
<circle
|
||||||
[ngStyle]="{'stroke-dashoffset.px': totpDash}"></circle>
|
class="totp-circle inner"
|
||||||
|
r="12.6"
|
||||||
|
cy="16"
|
||||||
|
cx="16"
|
||||||
|
[ngStyle]="{ 'stroke-dashoffset.px': totpDash }"
|
||||||
|
></circle>
|
||||||
<circle class="totp-circle outer" r="14" cy="16" cx="16"></circle>
|
<circle class="totp-circle outer" r="14" cy="16" cx="16"></circle>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyValue' | i18n}}"
|
<a
|
||||||
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
|
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -81,49 +146,79 @@
|
|||||||
<!-- Card -->
|
<!-- Card -->
|
||||||
<div *ngIf="cipher.card">
|
<div *ngIf="cipher.card">
|
||||||
<div class="box-content-row" *ngIf="cipher.card.cardholderName">
|
<div class="box-content-row" *ngIf="cipher.card.cardholderName">
|
||||||
<span class="row-label">{{'cardholderName' | i18n}}</span>
|
<span class="row-label">{{ "cardholderName" | i18n }}</span>
|
||||||
{{ cipher.card.cardholderName }}
|
{{ cipher.card.cardholderName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.number">
|
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.number">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label">{{'number' | i18n}}</span>
|
<span class="row-label">{{ "number" | i18n }}</span>
|
||||||
<span *ngIf="!showCardNumber" class="monospaced">{{ cipher.card.maskedNumber }}</span>
|
<span *ngIf="!showCardNumber" class="monospaced">{{ cipher.card.maskedNumber }}</span>
|
||||||
<span *ngIf="showCardNumber" class="monospaced">{{ cipher.card.number }}</span>
|
<span *ngIf="showCardNumber" class="monospaced">{{ cipher.card.number }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
<a
|
||||||
(click)="toggleCardNumber()" role="button">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber}"></i>
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="toggleCardNumber()"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyNumber' | i18n}}"
|
<a
|
||||||
(click)="copy(cipher.card.number, 'number', 'Card Number')" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyNumber' | i18n }}"
|
||||||
|
(click)="copy(cipher.card.number, 'number', 'Card Number')"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.card.brand">
|
<div class="box-content-row" *ngIf="cipher.card.brand">
|
||||||
<span class="row-label">{{'brand' | i18n}}</span>
|
<span class="row-label">{{ "brand" | i18n }}</span>
|
||||||
{{ cipher.card.brand }}
|
{{ cipher.card.brand }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.card.expiration">
|
<div class="box-content-row" *ngIf="cipher.card.expiration">
|
||||||
<span class="row-label">{{'expiration' | i18n}}</span>
|
<span class="row-label">{{ "expiration" | i18n }}</span>
|
||||||
{{ cipher.card.expiration }}
|
{{ cipher.card.expiration }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.code">
|
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.code">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label">{{'securityCode' | i18n}}</span>
|
<span class="row-label">{{ "securityCode" | i18n }}</span>
|
||||||
<span *ngIf="!showCardCode" class="monospaced">{{ cipher.card.maskedCode }}</span>
|
<span *ngIf="!showCardCode" class="monospaced">{{ cipher.card.maskedCode }}</span>
|
||||||
<span *ngIf="showCardCode" class="monospaced">{{ cipher.card.code }}</span>
|
<span *ngIf="showCardCode" class="monospaced">{{ cipher.card.code }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
<a
|
||||||
(click)="toggleCardCode()" role="button">
|
class="row-btn"
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
href="#"
|
||||||
[ngClass]="{'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode}"></i>
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
(click)="toggleCardCode()"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode }"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copySecurityCode' | i18n}}"
|
<a
|
||||||
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copySecurityCode' | i18n }}"
|
||||||
|
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -132,44 +227,48 @@
|
|||||||
<!-- Identity -->
|
<!-- Identity -->
|
||||||
<div *ngIf="cipher.identity">
|
<div *ngIf="cipher.identity">
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.fullName">
|
<div class="box-content-row" *ngIf="cipher.identity.fullName">
|
||||||
<span class="row-label">{{'identityName' | i18n}}</span>
|
<span class="row-label">{{ "identityName" | i18n }}</span>
|
||||||
{{ cipher.identity.fullName }}
|
{{ cipher.identity.fullName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.username">
|
<div class="box-content-row" *ngIf="cipher.identity.username">
|
||||||
<span class="row-label">{{'username' | i18n}}</span>
|
<span class="row-label">{{ "username" | i18n }}</span>
|
||||||
{{ cipher.identity.username }}
|
{{ cipher.identity.username }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.company">
|
<div class="box-content-row" *ngIf="cipher.identity.company">
|
||||||
<span class="row-label">{{'company' | i18n}}</span>
|
<span class="row-label">{{ "company" | i18n }}</span>
|
||||||
{{ cipher.identity.company }}
|
{{ cipher.identity.company }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.ssn">
|
<div class="box-content-row" *ngIf="cipher.identity.ssn">
|
||||||
<span class="row-label">{{'ssn' | i18n}}</span>
|
<span class="row-label">{{ "ssn" | i18n }}</span>
|
||||||
{{ cipher.identity.ssn }}
|
{{ cipher.identity.ssn }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.passportNumber">
|
<div class="box-content-row" *ngIf="cipher.identity.passportNumber">
|
||||||
<span class="row-label">{{'passportNumber' | i18n}}</span>
|
<span class="row-label">{{ "passportNumber" | i18n }}</span>
|
||||||
{{ cipher.identity.passportNumber }}
|
{{ cipher.identity.passportNumber }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.licenseNumber">
|
<div class="box-content-row" *ngIf="cipher.identity.licenseNumber">
|
||||||
<span class="row-label">{{'licenseNumber' | i18n}}</span>
|
<span class="row-label">{{ "licenseNumber" | i18n }}</span>
|
||||||
{{ cipher.identity.licenseNumber }}
|
{{ cipher.identity.licenseNumber }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.email">
|
<div class="box-content-row" *ngIf="cipher.identity.email">
|
||||||
<span class="row-label">{{'email' | i18n}}</span>
|
<span class="row-label">{{ "email" | i18n }}</span>
|
||||||
{{ cipher.identity.email }}
|
{{ cipher.identity.email }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.identity.phone">
|
<div class="box-content-row" *ngIf="cipher.identity.phone">
|
||||||
<span class="row-label">{{'phone' | i18n}}</span>
|
<span class="row-label">{{ "phone" | i18n }}</span>
|
||||||
{{ cipher.identity.phone }}
|
{{ cipher.identity.phone }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row"
|
<div
|
||||||
*ngIf="cipher.identity.address1 || cipher.identity.city || cipher.identity.country">
|
class="box-content-row"
|
||||||
<span class="row-label">{{'address' | i18n}}</span>
|
*ngIf="cipher.identity.address1 || cipher.identity.city || cipher.identity.country"
|
||||||
|
>
|
||||||
|
<span class="row-label">{{ "address" | i18n }}</span>
|
||||||
<div *ngIf="cipher.identity.address1">{{ cipher.identity.address1 }}</div>
|
<div *ngIf="cipher.identity.address1">{{ cipher.identity.address1 }}</div>
|
||||||
<div *ngIf="cipher.identity.address2">{{ cipher.identity.address2 }}</div>
|
<div *ngIf="cipher.identity.address2">{{ cipher.identity.address2 }}</div>
|
||||||
<div *ngIf="cipher.identity.address3">{{ cipher.identity.address3 }}</div>
|
<div *ngIf="cipher.identity.address3">{{ cipher.identity.address3 }}</div>
|
||||||
<div *ngIf="cipher.identity.fullAddressPart2">{{cipher.identity.fullAddressPart2}}</div>
|
<div *ngIf="cipher.identity.fullAddressPart2">
|
||||||
|
{{ cipher.identity.fullAddressPart2 }}
|
||||||
|
</div>
|
||||||
<div *ngIf="cipher.identity.country">{{ cipher.identity.country }}</div>
|
<div *ngIf="cipher.identity.country">{{ cipher.identity.country }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -177,19 +276,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="cipher.login && cipher.login.hasUris">
|
<div class="box" *ngIf="cipher.login && cipher.login.hasUris">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" *ngFor="let u of cipher.login.uris; let i = index">
|
<div
|
||||||
|
class="box-content-row box-content-row-flex"
|
||||||
|
*ngFor="let u of cipher.login.uris; let i = index"
|
||||||
|
>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<span class="row-label" *ngIf="!u.isWebsite">{{'uri' | i18n}}</span>
|
<span class="row-label" *ngIf="!u.isWebsite">{{ "uri" | i18n }}</span>
|
||||||
<span class="row-label" *ngIf="u.isWebsite">{{'website' | i18n}}</span>
|
<span class="row-label" *ngIf="u.isWebsite">{{ "website" | i18n }}</span>
|
||||||
<span title="{{ u.uri }}">{{ u.hostOrUri }}</span>
|
<span title="{{ u.uri }}">{{ u.hostOrUri }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'launch' | i18n}}" *ngIf="u.canLaunch"
|
<a
|
||||||
(click)="launch(u)" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'launch' | i18n }}"
|
||||||
|
*ngIf="u.canLaunch"
|
||||||
|
(click)="launch(u)"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-share-square-o" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-share-square-o" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyUri' | i18n}}"
|
<a
|
||||||
(click)="copy(u.uri, u.isWebsite ? 'website' : 'uri', 'URI')" role="button">
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyUri' | i18n }}"
|
||||||
|
(click)="copy(u.uri, u.isWebsite ? 'website' : 'uri', 'URI')"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -198,45 +313,66 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="cipher.notes">
|
<div class="box" *ngIf="cipher.notes">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'notes' | i18n}}
|
{{ "notes" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row pre-wrap">{{ cipher.notes }}</div>
|
<div class="box-content-row pre-wrap">{{ cipher.notes }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-vault-view-custom-fields *ngIf="cipher.hasFields" [cipher]="cipher"
|
<app-vault-view-custom-fields
|
||||||
[promptPassword]="promptPassword.bind(this)" [copy]="copy.bind(this)">
|
*ngIf="cipher.hasFields"
|
||||||
|
[cipher]="cipher"
|
||||||
|
[promptPassword]="promptPassword.bind(this)"
|
||||||
|
[copy]="copy.bind(this)"
|
||||||
|
>
|
||||||
</app-vault-view-custom-fields>
|
</app-vault-view-custom-fields>
|
||||||
<div class="box" *ngIf="cipher.hasAttachments && (canAccessPremium || cipher.organizationId)">
|
<div class="box" *ngIf="cipher.hasAttachments && (canAccessPremium || cipher.organizationId)">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
{{'attachments' | i18n}}
|
{{ "attachments" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<a class="box-content-row box-content-row-flex text-default"
|
<a
|
||||||
*ngFor="let attachment of cipher.attachments" href="#" appStopClick appBlurCLick
|
class="box-content-row box-content-row-flex text-default"
|
||||||
(click)="downloadAttachment(attachment)">
|
*ngFor="let attachment of cipher.attachments"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurCLick
|
||||||
|
(click)="downloadAttachment(attachment)"
|
||||||
|
>
|
||||||
<span class="row-main">{{ attachment.fileName }}</span>
|
<span class="row-main">{{ attachment.fileName }}</span>
|
||||||
<small class="row-sub-label">{{ attachment.sizeName }}</small>
|
<small class="row-sub-label">{{ attachment.sizeName }}</small>
|
||||||
<i class="fa fa-download fa-fw row-sub-icon" *ngIf="!attachment.downloading" aria-hidden="true"></i>
|
<i
|
||||||
<i class="fa fa-spinner fa-fw fa-spin row-sub-icon" *ngIf="attachment.downloading"
|
class="fa fa-download fa-fw row-sub-icon"
|
||||||
aria-hidden="true"></i>
|
*ngIf="!attachment.downloading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
class="fa fa-spinner fa-fw fa-spin row-sub-icon"
|
||||||
|
*ngIf="attachment.downloading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<div>
|
<div>
|
||||||
<b class="font-weight-semibold">{{'dateUpdated' | i18n}}:</b>
|
<b class="font-weight-semibold">{{ "dateUpdated" | i18n }}:</b>
|
||||||
{{cipher.revisionDate | date:'medium'}}
|
{{ cipher.revisionDate | date: "medium" }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="cipher.passwordRevisionDisplayDate">
|
<div *ngIf="cipher.passwordRevisionDisplayDate">
|
||||||
<b class="font-weight-semibold">{{'datePasswordUpdated' | i18n}}:</b>
|
<b class="font-weight-semibold">{{ "datePasswordUpdated" | i18n }}:</b>
|
||||||
{{cipher.passwordRevisionDisplayDate | date:'medium'}}
|
{{ cipher.passwordRevisionDisplayDate | date: "medium" }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="cipher.hasPasswordHistory">
|
<div *ngIf="cipher.hasPasswordHistory">
|
||||||
<b class="font-weight-semibold">{{'passwordHistory' | i18n}}:</b>
|
<b class="font-weight-semibold">{{ "passwordHistory" | i18n }}:</b>
|
||||||
<a href="#" (click)="viewHistory()" appStopClick role="button"
|
<a
|
||||||
appA11yTitle="{{'passwordHistory' | i18n}}, {{cipher.passwordHistory.length}}">
|
href="#"
|
||||||
|
(click)="viewHistory()"
|
||||||
|
appStopClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'passwordHistory' | i18n }}, {{ cipher.passwordHistory.length }}"
|
||||||
|
>
|
||||||
<span aria-hidden="true">{{ cipher.passwordHistory.length }}</span>
|
<span aria-hidden="true">{{ cipher.passwordHistory.length }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -245,20 +381,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer" *ngIf="cipher">
|
<div class="footer" *ngIf="cipher">
|
||||||
<button appBlurClick class="primary" (click)="edit()" appA11yTitle="{{'edit' | i18n}}" *ngIf="!cipher.isDeleted">
|
<button
|
||||||
|
appBlurClick
|
||||||
|
class="primary"
|
||||||
|
(click)="edit()"
|
||||||
|
appA11yTitle="{{ 'edit' | i18n }}"
|
||||||
|
*ngIf="!cipher.isDeleted"
|
||||||
|
>
|
||||||
<i class="fa fa-pencil fa-fw fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-pencil fa-fw fa-lg" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button appBlurClick class="primary" (click)="restore()" appA11yTitle="{{'restore' | i18n}}"
|
<button
|
||||||
*ngIf="cipher.isDeleted">
|
appBlurClick
|
||||||
|
class="primary"
|
||||||
|
(click)="restore()"
|
||||||
|
appA11yTitle="{{ 'restore' | i18n }}"
|
||||||
|
*ngIf="cipher.isDeleted"
|
||||||
|
>
|
||||||
<i class="fa fa-undo fa-fw fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-undo fa-fw fa-lg" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button appBlurClick class="primary" *ngIf="!cipher?.organizationId && !cipher.isDeleted" (click)="clone()"
|
<button
|
||||||
appA11yTitle="{{'clone' | i18n}}">
|
appBlurClick
|
||||||
|
class="primary"
|
||||||
|
*ngIf="!cipher?.organizationId && !cipher.isDeleted"
|
||||||
|
(click)="clone()"
|
||||||
|
appA11yTitle="{{ 'clone' | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-files-o fa-fw fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-files-o fa-fw fa-lg" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button appBlurClick type="button" (click)="delete()" class="danger"
|
<button
|
||||||
appA11yTitle="{{(cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n}}">
|
appBlurClick
|
||||||
|
type="button"
|
||||||
|
(click)="delete()"
|
||||||
|
class="danger"
|
||||||
|
appA11yTitle="{{ (cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n }}"
|
||||||
|
>
|
||||||
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,54 +5,79 @@ import {
|
|||||||
NgZone,
|
NgZone,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from "@angular/core";
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuditService } from 'jslib-common/abstractions/audit.service';
|
import { AuditService } from "jslib-common/abstractions/audit.service";
|
||||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from "jslib-common/abstractions/event.service";
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { TokenService } from 'jslib-common/abstractions/token.service';
|
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||||
|
|
||||||
import { ViewComponent as BaseViewComponent } from 'jslib-angular/components/view.component';
|
import { ViewComponent as BaseViewComponent } from "jslib-angular/components/view.component";
|
||||||
|
|
||||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = 'ViewComponent';
|
const BroadcasterSubscriptionId = "ViewComponent";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-view',
|
selector: "app-vault-view",
|
||||||
templateUrl: 'view.component.html',
|
templateUrl: "view.component.html",
|
||||||
})
|
})
|
||||||
export class ViewComponent extends BaseViewComponent implements OnChanges {
|
export class ViewComponent extends BaseViewComponent implements OnChanges {
|
||||||
@Output() onViewCipherPasswordHistory = new EventEmitter<CipherView>();
|
@Output() onViewCipherPasswordHistory = new EventEmitter<CipherView>();
|
||||||
|
|
||||||
constructor(cipherService: CipherService, totpService: TotpService,
|
constructor(
|
||||||
tokenService: TokenService, i18nService: I18nService,
|
cipherService: CipherService,
|
||||||
cryptoService: CryptoService, platformUtilsService: PlatformUtilsService,
|
totpService: TotpService,
|
||||||
auditService: AuditService, broadcasterService: BroadcasterService,
|
tokenService: TokenService,
|
||||||
ngZone: NgZone, changeDetectorRef: ChangeDetectorRef,
|
i18nService: I18nService,
|
||||||
eventService: EventService, apiService: ApiService,
|
cryptoService: CryptoService,
|
||||||
private messagingService: MessagingService, passwordRepromptService: PasswordRepromptService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
logService: LogService, stateService: StateService) {
|
auditService: AuditService,
|
||||||
super(cipherService, totpService, tokenService, i18nService, cryptoService, platformUtilsService,
|
broadcasterService: BroadcasterService,
|
||||||
auditService, window, broadcasterService, ngZone, changeDetectorRef, eventService,
|
ngZone: NgZone,
|
||||||
apiService, passwordRepromptService, logService, stateService);
|
changeDetectorRef: ChangeDetectorRef,
|
||||||
|
eventService: EventService,
|
||||||
|
apiService: ApiService,
|
||||||
|
private messagingService: MessagingService,
|
||||||
|
passwordRepromptService: PasswordRepromptService,
|
||||||
|
logService: LogService,
|
||||||
|
stateService: StateService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
cipherService,
|
||||||
|
totpService,
|
||||||
|
tokenService,
|
||||||
|
i18nService,
|
||||||
|
cryptoService,
|
||||||
|
platformUtilsService,
|
||||||
|
auditService,
|
||||||
|
window,
|
||||||
|
broadcasterService,
|
||||||
|
ngZone,
|
||||||
|
changeDetectorRef,
|
||||||
|
eventService,
|
||||||
|
apiService,
|
||||||
|
passwordRepromptService,
|
||||||
|
logService,
|
||||||
|
stateService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case 'windowHidden':
|
case "windowHidden":
|
||||||
this.onWindowHidden();
|
this.onWindowHidden();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -76,7 +101,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
|
|||||||
|
|
||||||
async copy(value: string, typeI18nKey: string, aType: string) {
|
async copy(value: string, typeI18nKey: string, aType: string) {
|
||||||
super.copy(value, typeI18nKey, aType);
|
super.copy(value, typeI18nKey, aType);
|
||||||
this.messagingService.send('minimizeOnCopy');
|
this.messagingService.send("minimizeOnCopy");
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowHidden() {
|
onWindowHidden() {
|
||||||
@ -84,7 +109,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
|
|||||||
this.showCardNumber = false;
|
this.showCardNumber = false;
|
||||||
this.showCardCode = false;
|
this.showCardCode = false;
|
||||||
if (this.cipher !== null && this.cipher.hasFields) {
|
if (this.cipher !== null && this.cipher.hasFields) {
|
||||||
this.cipher.fields.forEach(field => {
|
this.cipher.fields.forEach((field) => {
|
||||||
field.showValue = false;
|
field.showValue = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
18
src/entry.ts
18
src/entry.ts
@ -1,4 +1,4 @@
|
|||||||
import { NativeMessagingProxy } from './proxy/native-messaging-proxy';
|
import { NativeMessagingProxy } from "./proxy/native-messaging-proxy";
|
||||||
|
|
||||||
// We need to import the other dependencies using `require` since `import` will
|
// We need to import the other dependencies using `require` since `import` will
|
||||||
// generate `Error: Cannot find module 'electron'`. The cause of this error is
|
// generate `Error: Cannot find module 'electron'`. The cause of this error is
|
||||||
@ -6,18 +6,20 @@ import { NativeMessagingProxy } from './proxy/native-messaging-proxy';
|
|||||||
// which removes the electron module. This flag is needed for stdin/out to work
|
// which removes the electron module. This flag is needed for stdin/out to work
|
||||||
// properly on Windows.
|
// properly on Windows.
|
||||||
|
|
||||||
if (process.argv.some(arg => arg.indexOf('chrome-extension://') !== -1 || arg.indexOf('{') !== -1)) {
|
if (
|
||||||
if (process.platform === 'darwin') {
|
process.argv.some((arg) => arg.indexOf("chrome-extension://") !== -1 || arg.indexOf("{") !== -1)
|
||||||
|
) {
|
||||||
|
if (process.platform === "darwin") {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
const app = require('electron').app;
|
const app = require("electron").app;
|
||||||
|
|
||||||
app.on('ready', () => {
|
app.on("ready", () => {
|
||||||
app.dock.hide();
|
app.dock.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
process.stdout.on('error', e => {
|
process.stdout.on("error", (e) => {
|
||||||
if (e.code === 'EPIPE') {
|
if (e.code === "EPIPE") {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -26,7 +28,7 @@ if (process.argv.some(arg => arg.indexOf('chrome-extension://') !== -1 || arg.in
|
|||||||
proxy.run();
|
proxy.run();
|
||||||
} else {
|
} else {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
const Main = require('./main').Main;
|
const Main = require("./main").Main;
|
||||||
|
|
||||||
const main = new Main();
|
const main = new Main();
|
||||||
main.bootstrap();
|
main.bootstrap();
|
||||||
|
2
src/global.d.ts
vendored
2
src/global.d.ts
vendored
@ -1 +1 @@
|
|||||||
declare module 'forcefocus';
|
declare module "forcefocus";
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline';
|
<meta
|
||||||
img-src 'self' data: *; child-src *; frame-src *; connect-src *;">
|
http-equiv="Content-Security-Policy"
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
content="default-src 'self'; style-src 'self' 'unsafe-inline';
|
||||||
|
img-src 'self' data: *; child-src *; frame-src *; connect-src *;"
|
||||||
|
/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Bitwarden</title>
|
<title>Bitwarden</title>
|
||||||
<base href="">
|
<base href="" />
|
||||||
</head>
|
</head>
|
||||||
<body class="layout_frontend">
|
<body class="layout_frontend">
|
||||||
<app-root>
|
<app-root>
|
||||||
|
158
src/main.ts
158
src/main.ts
@ -1,26 +1,26 @@
|
|||||||
import { app } from 'electron';
|
import { app } from "electron";
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
|
|
||||||
import { I18nService } from './services/i18n.service';
|
import { I18nService } from "./services/i18n.service";
|
||||||
|
|
||||||
import { MenuMain } from './main/menu.main';
|
import { MenuMain } from "./main/menu.main";
|
||||||
import { MessagingMain } from './main/messaging.main';
|
import { MessagingMain } from "./main/messaging.main";
|
||||||
import { PowerMonitorMain } from './main/powerMonitor.main';
|
import { PowerMonitorMain } from "./main/powerMonitor.main";
|
||||||
|
|
||||||
import { BiometricMain } from 'jslib-common/abstractions/biometric.main';
|
import { BiometricMain } from "jslib-common/abstractions/biometric.main";
|
||||||
|
|
||||||
import { KeytarStorageListener } from 'jslib-electron/keytarStorageListener';
|
import { KeytarStorageListener } from "jslib-electron/keytarStorageListener";
|
||||||
|
|
||||||
import { ElectronLogService } from 'jslib-electron/services/electronLog.service';
|
import { ElectronLogService } from "jslib-electron/services/electronLog.service";
|
||||||
import { ElectronMainMessagingService } from 'jslib-electron/services/electronMainMessaging.service';
|
import { ElectronMainMessagingService } from "jslib-electron/services/electronMainMessaging.service";
|
||||||
import { ElectronStorageService } from 'jslib-electron/services/electronStorage.service';
|
import { ElectronStorageService } from "jslib-electron/services/electronStorage.service";
|
||||||
|
|
||||||
import { TrayMain } from 'jslib-electron/tray.main';
|
import { TrayMain } from "jslib-electron/tray.main";
|
||||||
import { UpdaterMain } from 'jslib-electron/updater.main';
|
import { UpdaterMain } from "jslib-electron/updater.main";
|
||||||
import { WindowMain } from 'jslib-electron/window.main';
|
import { WindowMain } from "jslib-electron/window.main";
|
||||||
import { NativeMessagingMain } from './main/nativeMessaging.main';
|
import { NativeMessagingMain } from "./main/nativeMessaging.main";
|
||||||
|
|
||||||
import { StateService } from 'jslib-common/services/state.service';
|
import { StateService } from "jslib-common/services/state.service";
|
||||||
|
|
||||||
export class Main {
|
export class Main {
|
||||||
logService: ElectronLogService;
|
logService: ElectronLogService;
|
||||||
@ -44,83 +44,109 @@ export class Main {
|
|||||||
let appDataPath = null;
|
let appDataPath = null;
|
||||||
if (process.env.BITWARDEN_APPDATA_DIR != null) {
|
if (process.env.BITWARDEN_APPDATA_DIR != null) {
|
||||||
appDataPath = process.env.BITWARDEN_APPDATA_DIR;
|
appDataPath = process.env.BITWARDEN_APPDATA_DIR;
|
||||||
} else if (process.platform === 'win32' && process.env.PORTABLE_EXECUTABLE_DIR != null) {
|
} else if (process.platform === "win32" && process.env.PORTABLE_EXECUTABLE_DIR != null) {
|
||||||
appDataPath = path.join(process.env.PORTABLE_EXECUTABLE_DIR, 'bitwarden-appdata');
|
appDataPath = path.join(process.env.PORTABLE_EXECUTABLE_DIR, "bitwarden-appdata");
|
||||||
} else if (process.platform === 'linux' && process.env.SNAP_USER_DATA != null) {
|
} else if (process.platform === "linux" && process.env.SNAP_USER_DATA != null) {
|
||||||
appDataPath = path.join(process.env.SNAP_USER_DATA, 'appdata');
|
appDataPath = path.join(process.env.SNAP_USER_DATA, "appdata");
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', () => {
|
app.on("ready", () => {
|
||||||
// on ready stuff...
|
// on ready stuff...
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appDataPath != null) {
|
if (appDataPath != null) {
|
||||||
app.setPath('userData', appDataPath);
|
app.setPath("userData", appDataPath);
|
||||||
}
|
}
|
||||||
app.setPath('logs', path.join(app.getPath('userData'), 'logs'));
|
app.setPath("logs", path.join(app.getPath("userData"), "logs"));
|
||||||
|
|
||||||
const args = process.argv.slice(1);
|
const args = process.argv.slice(1);
|
||||||
const watch = args.some(val => val === '--watch');
|
const watch = args.some((val) => val === "--watch");
|
||||||
|
|
||||||
if (watch) {
|
if (watch) {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
require('electron-reload')(__dirname, {});
|
require("electron-reload")(__dirname, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logService = new ElectronLogService(null, app.getPath('userData'));
|
this.logService = new ElectronLogService(null, app.getPath("userData"));
|
||||||
this.i18nService = new I18nService('en', './locales/');
|
this.i18nService = new I18nService("en", "./locales/");
|
||||||
|
|
||||||
const storageDefaults: any = {};
|
const storageDefaults: any = {};
|
||||||
// Default vault timeout to "on restart", and action to "lock"
|
// Default vault timeout to "on restart", and action to "lock"
|
||||||
storageDefaults['global.vaultTimeout'] = -1;
|
storageDefaults["global.vaultTimeout"] = -1;
|
||||||
storageDefaults['global.vaultTimeoutAction'] = 'lock';
|
storageDefaults["global.vaultTimeoutAction"] = "lock";
|
||||||
this.storageService = new ElectronStorageService(app.getPath('userData'), storageDefaults);
|
this.storageService = new ElectronStorageService(app.getPath("userData"), storageDefaults);
|
||||||
|
|
||||||
// TODO: this state service will have access to on disk storage, but not in memory storage.
|
// TODO: this state service will have access to on disk storage, but not in memory storage.
|
||||||
// If we could get this to work using the stateService singleton that the rest of the app uses we could save
|
// If we could get this to work using the stateService singleton that the rest of the app uses we could save
|
||||||
// ourselves from some hacks, like having to manually update the app menu vs. the menu subscribing to events.
|
// ourselves from some hacks, like having to manually update the app menu vs. the menu subscribing to events.
|
||||||
this.stateService = new StateService(this.storageService, null, this.logService, null);
|
this.stateService = new StateService(this.storageService, null, this.logService, null);
|
||||||
|
|
||||||
this.windowMain = new WindowMain(this.stateService, this.logService, true, undefined, undefined,
|
this.windowMain = new WindowMain(
|
||||||
arg => this.processDeepLink(arg), win => this.trayMain.setupWindowListeners(win));
|
this.stateService,
|
||||||
|
this.logService,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
(arg) => this.processDeepLink(arg),
|
||||||
|
(win) => this.trayMain.setupWindowListeners(win)
|
||||||
|
);
|
||||||
this.messagingMain = new MessagingMain(this, this.stateService);
|
this.messagingMain = new MessagingMain(this, this.stateService);
|
||||||
this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, 'desktop',
|
this.updaterMain = new UpdaterMain(
|
||||||
null, null, null, 'bitwarden');
|
this.i18nService,
|
||||||
|
this.windowMain,
|
||||||
|
"desktop",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"bitwarden"
|
||||||
|
);
|
||||||
this.menuMain = new MenuMain(this);
|
this.menuMain = new MenuMain(this);
|
||||||
this.powerMonitorMain = new PowerMonitorMain(this);
|
this.powerMonitorMain = new PowerMonitorMain(this);
|
||||||
this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.stateService);
|
this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.stateService);
|
||||||
|
|
||||||
this.messagingService = new ElectronMainMessagingService(this.windowMain, message => {
|
this.messagingService = new ElectronMainMessagingService(this.windowMain, (message) => {
|
||||||
this.messagingMain.onMessage(message);
|
this.messagingMain.onMessage(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
if (process.platform === 'win32') {
|
const BiometricWindowsMain = require("jslib-electron/biometric.windows.main").default;
|
||||||
const BiometricWindowsMain = require('jslib-electron/biometric.windows.main').default;
|
this.biometricMain = new BiometricWindowsMain(
|
||||||
this.biometricMain = new BiometricWindowsMain(this.i18nService, this.windowMain, this.stateService, this.logService);
|
this.i18nService,
|
||||||
} else if (process.platform === 'darwin') {
|
this.windowMain,
|
||||||
const BiometricDarwinMain = require('jslib-electron/biometric.darwin.main').default;
|
this.stateService,
|
||||||
|
this.logService
|
||||||
|
);
|
||||||
|
} else if (process.platform === "darwin") {
|
||||||
|
const BiometricDarwinMain = require("jslib-electron/biometric.darwin.main").default;
|
||||||
this.biometricMain = new BiometricDarwinMain(this.i18nService, this.stateService);
|
this.biometricMain = new BiometricDarwinMain(this.i18nService, this.stateService);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.keytarStorageListener = new KeytarStorageListener('Bitwarden', this.biometricMain);
|
this.keytarStorageListener = new KeytarStorageListener("Bitwarden", this.biometricMain);
|
||||||
|
|
||||||
this.nativeMessagingMain = new NativeMessagingMain(this.logService, this.windowMain, app.getPath('userData'), app.getPath('exe'));
|
this.nativeMessagingMain = new NativeMessagingMain(
|
||||||
|
this.logService,
|
||||||
|
this.windowMain,
|
||||||
|
app.getPath("userData"),
|
||||||
|
app.getPath("exe")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
this.keytarStorageListener.init();
|
this.keytarStorageListener.init();
|
||||||
this.windowMain.init().then(async () => {
|
this.windowMain.init().then(
|
||||||
|
async () => {
|
||||||
const locale = await this.stateService.getLocale();
|
const locale = await this.stateService.getLocale();
|
||||||
await this.i18nService.init(locale != null ? locale : app.getLocale());
|
await this.i18nService.init(locale != null ? locale : app.getLocale());
|
||||||
this.messagingMain.init();
|
this.messagingMain.init();
|
||||||
this.menuMain.init();
|
this.menuMain.init();
|
||||||
await this.trayMain.init('Bitwarden', [{
|
await this.trayMain.init("Bitwarden", [
|
||||||
label: this.i18nService.t('lockNow'),
|
{
|
||||||
|
label: this.i18nService.t("lockNow"),
|
||||||
enabled: false,
|
enabled: false,
|
||||||
id: 'lockNow',
|
id: "lockNow",
|
||||||
click: () => this.messagingService.send('lockVault'),
|
click: () => this.messagingService.send("lockVault"),
|
||||||
}]);
|
},
|
||||||
|
]);
|
||||||
if (await this.stateService.getEnableStartToTray()) {
|
if (await this.stateService.getEnableStartToTray()) {
|
||||||
this.trayMain.hideToTray();
|
this.trayMain.hideToTray();
|
||||||
}
|
}
|
||||||
@ -134,43 +160,47 @@ export class Main {
|
|||||||
this.nativeMessagingMain.listen();
|
this.nativeMessagingMain.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.removeAsDefaultProtocolClient('bitwarden');
|
app.removeAsDefaultProtocolClient("bitwarden");
|
||||||
if (process.env.NODE_ENV === 'development' && process.platform === 'win32') {
|
if (process.env.NODE_ENV === "development" && process.platform === "win32") {
|
||||||
// Fix development build on Windows requirering a different protocol client
|
// Fix development build on Windows requirering a different protocol client
|
||||||
app.setAsDefaultProtocolClient('bitwarden', process.execPath, [
|
app.setAsDefaultProtocolClient("bitwarden", process.execPath, [
|
||||||
process.argv[1],
|
process.argv[1],
|
||||||
path.resolve(process.argv[2]),
|
path.resolve(process.argv[2]),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
app.setAsDefaultProtocolClient('bitwarden');
|
app.setAsDefaultProtocolClient("bitwarden");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process protocol for macOS
|
// Process protocol for macOS
|
||||||
app.on('open-url', (event, url) => {
|
app.on("open-url", (event, url) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.processDeepLink([url]);
|
this.processDeepLink([url]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle window visibility events
|
// Handle window visibility events
|
||||||
this.windowMain.win.on('hide', () => {
|
this.windowMain.win.on("hide", () => {
|
||||||
this.messagingService.send('windowHidden');
|
this.messagingService.send("windowHidden");
|
||||||
});
|
});
|
||||||
this.windowMain.win.on('minimize', () => {
|
this.windowMain.win.on("minimize", () => {
|
||||||
this.messagingService.send('windowHidden');
|
this.messagingService.send("windowHidden");
|
||||||
});
|
});
|
||||||
}, (e: any) => {
|
},
|
||||||
|
(e: any) => {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private processDeepLink(argv: string[]): void {
|
private processDeepLink(argv: string[]): void {
|
||||||
argv.filter(s => s.indexOf('bitwarden://') === 0).forEach(s => {
|
argv
|
||||||
|
.filter((s) => s.indexOf("bitwarden://") === 0)
|
||||||
|
.forEach((s) => {
|
||||||
const url = new URL(s);
|
const url = new URL(s);
|
||||||
const code = url.searchParams.get('code');
|
const code = url.searchParams.get("code");
|
||||||
const receivedState = url.searchParams.get('state');
|
const receivedState = url.searchParams.get("state");
|
||||||
if (code != null && receivedState != null) {
|
if (code != null && receivedState != null) {
|
||||||
this.messagingService.send('ssoCallback', { code: code, state: receivedState });
|
this.messagingService.send("ssoCallback", { code: code, state: receivedState });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,25 @@
|
|||||||
import {
|
import { BrowserWindow, clipboard, dialog, MenuItemConstructorOptions } from "electron";
|
||||||
BrowserWindow,
|
|
||||||
clipboard,
|
|
||||||
dialog,
|
|
||||||
MenuItemConstructorOptions,
|
|
||||||
} from 'electron';
|
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
|
||||||
import { UpdaterMain } from 'jslib-electron/updater.main';
|
import { UpdaterMain } from "jslib-electron/updater.main";
|
||||||
import { isMac, isSnapStore, isWindowsStore } from 'jslib-electron/utils';
|
import { isMac, isSnapStore, isWindowsStore } from "jslib-electron/utils";
|
||||||
|
|
||||||
import { IMenubarMenu } from './menubar';
|
import { IMenubarMenu } from "./menubar";
|
||||||
|
|
||||||
export class AboutMenu implements IMenubarMenu {
|
export class AboutMenu implements IMenubarMenu {
|
||||||
readonly id: string = 'about';
|
readonly id: string = "about";
|
||||||
|
|
||||||
get visible(): boolean {
|
get visible(): boolean {
|
||||||
return !isMac();
|
return !isMac();
|
||||||
}
|
}
|
||||||
|
|
||||||
get label(): string {
|
get label(): string {
|
||||||
return this.localize('about');
|
return this.localize("about");
|
||||||
}
|
}
|
||||||
|
|
||||||
get items(): MenuItemConstructorOptions[] {
|
get items(): MenuItemConstructorOptions[] {
|
||||||
return [
|
return [this.separator, this.checkForUpdates, this.aboutBitwarden];
|
||||||
this.separator,
|
|
||||||
this.checkForUpdates,
|
|
||||||
this.aboutBitwarden,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _i18nService: I18nService;
|
private readonly _i18nService: I18nService;
|
||||||
@ -40,7 +31,7 @@ export class AboutMenu implements IMenubarMenu {
|
|||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
version: string,
|
version: string,
|
||||||
window: BrowserWindow,
|
window: BrowserWindow,
|
||||||
updater: UpdaterMain,
|
updater: UpdaterMain
|
||||||
) {
|
) {
|
||||||
this._i18nService = i18nService;
|
this._i18nService = i18nService;
|
||||||
this._updater = updater;
|
this._updater = updater;
|
||||||
@ -49,13 +40,13 @@ export class AboutMenu implements IMenubarMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get separator(): MenuItemConstructorOptions {
|
private get separator(): MenuItemConstructorOptions {
|
||||||
return { type: 'separator' };
|
return { type: "separator" };
|
||||||
}
|
}
|
||||||
|
|
||||||
private get checkForUpdates(): MenuItemConstructorOptions {
|
private get checkForUpdates(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'checkForUpdates',
|
id: "checkForUpdates",
|
||||||
label: this.localize('checkForUpdates'),
|
label: this.localize("checkForUpdates"),
|
||||||
visible: !isWindowsStore() && !isSnapStore(),
|
visible: !isWindowsStore() && !isSnapStore(),
|
||||||
click: () => this.checkForUpdate(),
|
click: () => this.checkForUpdate(),
|
||||||
};
|
};
|
||||||
@ -63,21 +54,26 @@ export class AboutMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get aboutBitwarden(): MenuItemConstructorOptions {
|
private get aboutBitwarden(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'aboutBitwarden',
|
id: "aboutBitwarden",
|
||||||
label: this.localize('aboutBitwarden'),
|
label: this.localize("aboutBitwarden"),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const aboutInformation = this.localize('version', this._version) +
|
const aboutInformation =
|
||||||
'\nShell ' + process.versions.electron +
|
this.localize("version", this._version) +
|
||||||
'\nRenderer ' + process.versions.chrome +
|
"\nShell " +
|
||||||
'\nNode ' + process.versions.node +
|
process.versions.electron +
|
||||||
'\nArchitecture ' + process.arch;
|
"\nRenderer " +
|
||||||
|
process.versions.chrome +
|
||||||
|
"\nNode " +
|
||||||
|
process.versions.node +
|
||||||
|
"\nArchitecture " +
|
||||||
|
process.arch;
|
||||||
const result = await dialog.showMessageBox(this._window, {
|
const result = await dialog.showMessageBox(this._window, {
|
||||||
title: 'Bitwarden',
|
title: "Bitwarden",
|
||||||
message: 'Bitwarden',
|
message: "Bitwarden",
|
||||||
detail: aboutInformation,
|
detail: aboutInformation,
|
||||||
type: 'info',
|
type: "info",
|
||||||
noLink: true,
|
noLink: true,
|
||||||
buttons: [this.localize('ok'), this.localize('copy')],
|
buttons: [this.localize("ok"), this.localize("copy")],
|
||||||
});
|
});
|
||||||
if (result.response === 1) {
|
if (result.response === 1) {
|
||||||
clipboard.writeText(aboutInformation);
|
clipboard.writeText(aboutInformation);
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
import {
|
import { BrowserWindow, dialog, MenuItemConstructorOptions, shell } from "electron";
|
||||||
BrowserWindow,
|
|
||||||
dialog,
|
|
||||||
MenuItemConstructorOptions,
|
|
||||||
shell,
|
|
||||||
} from 'electron';
|
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
|
|
||||||
import { isMacAppStore, isWindowsStore } from 'jslib-electron/utils';
|
import { isMacAppStore, isWindowsStore } from "jslib-electron/utils";
|
||||||
|
|
||||||
import { IMenubarMenu } from './menubar';
|
import { IMenubarMenu } from "./menubar";
|
||||||
|
|
||||||
export class AccountMenu implements IMenubarMenu {
|
export class AccountMenu implements IMenubarMenu {
|
||||||
readonly id: string = 'accountMenu';
|
readonly id: string = "accountMenu";
|
||||||
|
|
||||||
get label(): string {
|
get label(): string {
|
||||||
return this.localize('account');
|
return this.localize("account");
|
||||||
}
|
}
|
||||||
|
|
||||||
get items(): MenuItemConstructorOptions[] {
|
get items(): MenuItemConstructorOptions[] {
|
||||||
@ -39,7 +34,7 @@ export class AccountMenu implements IMenubarMenu {
|
|||||||
messagingService: MessagingService,
|
messagingService: MessagingService,
|
||||||
webVaultUrl: string,
|
webVaultUrl: string,
|
||||||
window: BrowserWindow,
|
window: BrowserWindow,
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean
|
||||||
) {
|
) {
|
||||||
this._i18nService = i18nService;
|
this._i18nService = i18nService;
|
||||||
this._messagingService = messagingService;
|
this._messagingService = messagingService;
|
||||||
@ -50,9 +45,9 @@ export class AccountMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get premiumMembership(): MenuItemConstructorOptions {
|
private get premiumMembership(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: this.localize('premiumMembership'),
|
label: this.localize("premiumMembership"),
|
||||||
click: () => this.sendMessage('openPremium'),
|
click: () => this.sendMessage("openPremium"),
|
||||||
id: 'premiumMembership',
|
id: "premiumMembership",
|
||||||
visible: !isWindowsStore() && !isMacAppStore(),
|
visible: !isWindowsStore() && !isMacAppStore(),
|
||||||
enabled: this._isAuthenticated,
|
enabled: this._isAuthenticated,
|
||||||
};
|
};
|
||||||
@ -60,14 +55,14 @@ export class AccountMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get changeMasterPassword(): MenuItemConstructorOptions {
|
private get changeMasterPassword(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: this.localize('changeMasterPass'),
|
label: this.localize("changeMasterPass"),
|
||||||
id: 'changeMasterPass',
|
id: "changeMasterPass",
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const result = await dialog.showMessageBox(this._window, {
|
const result = await dialog.showMessageBox(this._window, {
|
||||||
title: this.localize('changeMasterPass'),
|
title: this.localize("changeMasterPass"),
|
||||||
message: this.localize('changeMasterPass'),
|
message: this.localize("changeMasterPass"),
|
||||||
detail: this.localize('changeMasterPasswordConfirmation'),
|
detail: this.localize("changeMasterPasswordConfirmation"),
|
||||||
buttons: [this.localize('yes'), this.localize('no')],
|
buttons: [this.localize("yes"), this.localize("no")],
|
||||||
cancelId: 1,
|
cancelId: 1,
|
||||||
defaultId: 0,
|
defaultId: 0,
|
||||||
noLink: true,
|
noLink: true,
|
||||||
@ -82,14 +77,14 @@ export class AccountMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get twoStepLogin(): MenuItemConstructorOptions {
|
private get twoStepLogin(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: this.localize('twoStepLogin'),
|
label: this.localize("twoStepLogin"),
|
||||||
id: 'twoStepLogin',
|
id: "twoStepLogin",
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const result = await dialog.showMessageBox(this._window, {
|
const result = await dialog.showMessageBox(this._window, {
|
||||||
title: this.localize('twoStepLogin'),
|
title: this.localize("twoStepLogin"),
|
||||||
message: this.localize('twoStepLogin'),
|
message: this.localize("twoStepLogin"),
|
||||||
detail: this.localize('twoStepLoginConfirmation'),
|
detail: this.localize("twoStepLoginConfirmation"),
|
||||||
buttons: [this.localize('yes'), this.localize('no')],
|
buttons: [this.localize("yes"), this.localize("no")],
|
||||||
cancelId: 1,
|
cancelId: 1,
|
||||||
defaultId: 0,
|
defaultId: 0,
|
||||||
noLink: true,
|
noLink: true,
|
||||||
@ -104,9 +99,9 @@ export class AccountMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get fingerprintPhrase(): MenuItemConstructorOptions {
|
private get fingerprintPhrase(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: this.localize('fingerprintPhrase'),
|
label: this.localize("fingerprintPhrase"),
|
||||||
id: 'fingerprintPhrase',
|
id: "fingerprintPhrase",
|
||||||
click: () => this.sendMessage('showFingerprintPhrase'),
|
click: () => this.sendMessage("showFingerprintPhrase"),
|
||||||
enabled: this._isAuthenticated,
|
enabled: this._isAuthenticated,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
import {
|
import { BrowserWindow, dialog, MenuItem, MenuItemConstructorOptions } from "electron";
|
||||||
BrowserWindow,
|
|
||||||
dialog,
|
|
||||||
MenuItem,
|
|
||||||
MenuItemConstructorOptions,
|
|
||||||
} from 'electron';
|
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||||
|
|
||||||
import { UpdaterMain } from 'jslib-electron/updater.main';
|
import { UpdaterMain } from "jslib-electron/updater.main";
|
||||||
import { isMacAppStore, isSnapStore, isWindowsStore } from 'jslib-electron/utils';
|
import { isMacAppStore, isSnapStore, isWindowsStore } from "jslib-electron/utils";
|
||||||
|
|
||||||
import { IMenubarMenu } from './menubar';
|
import { IMenubarMenu } from "./menubar";
|
||||||
|
|
||||||
import { MenuAccount } from './menu.updater';
|
import { MenuAccount } from "./menu.updater";
|
||||||
|
|
||||||
// AKA: "FirstMenu" or "MacMenu" - the first menu that shows on all macOs apps
|
// AKA: "FirstMenu" or "MacMenu" - the first menu that shows on all macOs apps
|
||||||
export class BitwardenMenu implements IMenubarMenu {
|
export class BitwardenMenu implements IMenubarMenu {
|
||||||
readonly id: string = 'bitwarden';
|
readonly id: string = "bitwarden";
|
||||||
readonly label: string = 'Bitwarden';
|
readonly label: string = "Bitwarden";
|
||||||
|
|
||||||
get items(): MenuItemConstructorOptions[] {
|
get items(): MenuItemConstructorOptions[] {
|
||||||
return [
|
return [
|
||||||
@ -50,7 +45,7 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
messagingService: MessagingService,
|
messagingService: MessagingService,
|
||||||
updater: UpdaterMain,
|
updater: UpdaterMain,
|
||||||
window: BrowserWindow,
|
window: BrowserWindow,
|
||||||
accounts: { [userId: string]: MenuAccount },
|
accounts: { [userId: string]: MenuAccount }
|
||||||
) {
|
) {
|
||||||
this._i18nService = i18nService;
|
this._i18nService = i18nService;
|
||||||
this._updater = updater;
|
this._updater = updater;
|
||||||
@ -65,44 +60,41 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get aboutBitwarden(): MenuItemConstructorOptions {
|
private get aboutBitwarden(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'aboutBitwarden',
|
id: "aboutBitwarden",
|
||||||
label: this.localize('aboutBitwarden'),
|
label: this.localize("aboutBitwarden"),
|
||||||
role: 'about',
|
role: "about",
|
||||||
visible: isMacAppStore(),
|
visible: isMacAppStore(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get checkForUpdates(): MenuItemConstructorOptions {
|
private get checkForUpdates(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'checkForUpdates',
|
id: "checkForUpdates",
|
||||||
label: this.localize('checkForUpdates'),
|
label: this.localize("checkForUpdates"),
|
||||||
click: menuItem => this.checkForUpdate(menuItem),
|
click: (menuItem) => this.checkForUpdate(menuItem),
|
||||||
visible: !isMacAppStore() && !isWindowsStore() && !isSnapStore(),
|
visible: !isMacAppStore() && !isWindowsStore() && !isSnapStore(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get separator(): MenuItemConstructorOptions {
|
private get separator(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
type: 'separator',
|
type: "separator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get settings(): MenuItemConstructorOptions {
|
private get settings(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'settings',
|
id: "settings",
|
||||||
label: this.localize(process.platform === 'darwin' ?
|
label: this.localize(process.platform === "darwin" ? "preferences" : "settings"),
|
||||||
'preferences' :
|
click: () => this.sendMessage("openSettings"),
|
||||||
'settings'
|
accelerator: "CmdOrCtrl+,",
|
||||||
),
|
|
||||||
click: () => this.sendMessage('openSettings'),
|
|
||||||
accelerator: 'CmdOrCtrl+,',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get lock(): MenuItemConstructorOptions {
|
private get lock(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'lock',
|
id: "lock",
|
||||||
label: this.localize('lockVault'),
|
label: this.localize("lockVault"),
|
||||||
submenu: this.lockSubmenu,
|
submenu: this.lockSubmenu,
|
||||||
enabled: this.hasAccounts,
|
enabled: this.hasAccounts,
|
||||||
};
|
};
|
||||||
@ -118,7 +110,7 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
value.push({
|
value.push({
|
||||||
label: this._accounts[userId].email,
|
label: this._accounts[userId].email,
|
||||||
id: `lockNow_${this._accounts[userId].userId}`,
|
id: `lockNow_${this._accounts[userId].userId}`,
|
||||||
click: () => this.sendMessage('lockVault', { userId: this._accounts[userId].userId }),
|
click: () => this.sendMessage("lockVault", { userId: this._accounts[userId].userId }),
|
||||||
enabled: !this._accounts[userId].isLocked,
|
enabled: !this._accounts[userId].isLocked,
|
||||||
visible: this._accounts[userId].isAuthenticated,
|
visible: this._accounts[userId].isAuthenticated,
|
||||||
});
|
});
|
||||||
@ -128,18 +120,18 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get lockAll(): MenuItemConstructorOptions {
|
private get lockAll(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'lockAllNow',
|
id: "lockAllNow",
|
||||||
label: this.localize('lockAllVaults'),
|
label: this.localize("lockAllVaults"),
|
||||||
click: () => this.sendMessage('lockAllVaults'),
|
click: () => this.sendMessage("lockAllVaults"),
|
||||||
accelerator: 'CmdOrCtrl+L',
|
accelerator: "CmdOrCtrl+L",
|
||||||
enabled: this.hasAccounts,
|
enabled: this.hasAccounts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get logOut(): MenuItemConstructorOptions {
|
private get logOut(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'logOut',
|
id: "logOut",
|
||||||
label: this.localize('logOut'),
|
label: this.localize("logOut"),
|
||||||
submenu: this.logOutSubmenu,
|
submenu: this.logOutSubmenu,
|
||||||
enabled: this.hasAccounts,
|
enabled: this.hasAccounts,
|
||||||
};
|
};
|
||||||
@ -157,16 +149,16 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
id: `logOut_${this._accounts[userId].userId}`,
|
id: `logOut_${this._accounts[userId].userId}`,
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const result = await dialog.showMessageBox(this._window, {
|
const result = await dialog.showMessageBox(this._window, {
|
||||||
title: this.localize('logOut'),
|
title: this.localize("logOut"),
|
||||||
message: this.localize('logOut'),
|
message: this.localize("logOut"),
|
||||||
detail: this.localize('logOutConfirmation'),
|
detail: this.localize("logOutConfirmation"),
|
||||||
buttons: [this.localize('logOut'), this.localize('cancel')],
|
buttons: [this.localize("logOut"), this.localize("cancel")],
|
||||||
cancelId: 1,
|
cancelId: 1,
|
||||||
defaultId: 0,
|
defaultId: 0,
|
||||||
noLink: true,
|
noLink: true,
|
||||||
});
|
});
|
||||||
if (result.response === 0) {
|
if (result.response === 0) {
|
||||||
this.sendMessage('logout', { userId: this._accounts[userId].userId });
|
this.sendMessage("logout", { userId: this._accounts[userId].userId });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visible: this._accounts[userId].isAuthenticated,
|
visible: this._accounts[userId].isAuthenticated,
|
||||||
@ -177,9 +169,9 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get services(): MenuItemConstructorOptions {
|
private get services(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'services',
|
id: "services",
|
||||||
label: this.localize('services'),
|
label: this.localize("services"),
|
||||||
role: 'services',
|
role: "services",
|
||||||
submenu: [],
|
submenu: [],
|
||||||
visible: isMacAppStore(),
|
visible: isMacAppStore(),
|
||||||
};
|
};
|
||||||
@ -187,36 +179,36 @@ export class BitwardenMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get hideBitwarden(): MenuItemConstructorOptions {
|
private get hideBitwarden(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'hideBitwarden',
|
id: "hideBitwarden",
|
||||||
label: this.localize('hideBitwarden'),
|
label: this.localize("hideBitwarden"),
|
||||||
role: 'hide',
|
role: "hide",
|
||||||
visible: isMacAppStore(),
|
visible: isMacAppStore(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get hideOthers(): MenuItemConstructorOptions {
|
private get hideOthers(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'hideOthers',
|
id: "hideOthers",
|
||||||
label: this.localize('hideOthers'),
|
label: this.localize("hideOthers"),
|
||||||
role: 'hideOthers',
|
role: "hideOthers",
|
||||||
visible: isMacAppStore(),
|
visible: isMacAppStore(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get showAll(): MenuItemConstructorOptions {
|
private get showAll(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'showAll',
|
id: "showAll",
|
||||||
label: this.localize('showAll'),
|
label: this.localize("showAll"),
|
||||||
role: 'unhide',
|
role: "unhide",
|
||||||
visible: isMacAppStore(),
|
visible: isMacAppStore(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get quitBitwarden(): MenuItemConstructorOptions {
|
private get quitBitwarden(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: 'quitBitwarden',
|
id: "quitBitwarden",
|
||||||
label: this.localize('quitBitwarden'),
|
label: this.localize("quitBitwarden"),
|
||||||
role: 'quit',
|
role: "quit",
|
||||||
visible: isMacAppStore(),
|
visible: isMacAppStore(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user