mirror of
https://github.com/bitwarden/browser
synced 2025-12-30 15:13:32 +00:00
Username generator (#1456)
* username generator implemented * disable type when coming from add/edit * restyle buttons to new icon-btn * update generated-wrapper styles * only show policy messages for passwords * make generated-wrapper a standalone style * Update src/app/vault/password-generator.component.html Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * aria-expanded on show options Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-body form">
|
||||
<div class="box">
|
||||
<label class="settingsTitle">{{ "settingsTitle" | i18n: currentUserEmail }} </label>
|
||||
<div class="box-header">
|
||||
{{ "settingsTitle" | i18n: currentUserEmail }}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<h2>
|
||||
<button
|
||||
|
||||
@@ -317,10 +317,10 @@ export class AppComponent implements OnInit {
|
||||
case "newFolder":
|
||||
await this.addFolder();
|
||||
break;
|
||||
case "openPasswordGenerator":
|
||||
// openPasswordGenerator has extended functionality if called in the vault
|
||||
case "openGenerator":
|
||||
// openGenerator has extended functionality if called in the vault
|
||||
if (!this.router.url.includes("vault")) {
|
||||
await this.openPasswordGenerator();
|
||||
await this.openGenerator();
|
||||
}
|
||||
break;
|
||||
case "convertAccountToKeyConnector":
|
||||
@@ -402,14 +402,14 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
async openPasswordGenerator() {
|
||||
async openGenerator() {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
[this.modal] = await this.modalService.openViewRef(
|
||||
PasswordGeneratorComponent,
|
||||
this.folderAddEditModalRef,
|
||||
this.passwordGeneratorModalRef,
|
||||
(comp) => (comp.showSelect = false)
|
||||
);
|
||||
|
||||
|
||||
@@ -27,15 +27,30 @@
|
||||
</div>
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUsername">{{ "username" | i18n }}</label>
|
||||
<input
|
||||
id="loginUsername"
|
||||
type="text"
|
||||
name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="loginUsername">{{ "username" | i18n }}</label>
|
||||
<input
|
||||
id="loginUsername"
|
||||
type="text"
|
||||
name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'generateUsername' | i18n }}"
|
||||
(click)="generateUsername()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<div
|
||||
class="password-wrapper monospaced"
|
||||
class="generated-wrapper monospaced"
|
||||
appSelectCopy
|
||||
[innerHTML]="h.password | colorPassword"
|
||||
></div>
|
||||
|
||||
@@ -4,52 +4,84 @@
|
||||
aria-modal="true"
|
||||
attr.aria-label="{{ 'generatePassword' | i18n }}"
|
||||
>
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-dialog modal-md" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect()">
|
||||
<div class="modal-title">
|
||||
{{ "generator" | i18n }}
|
||||
</div>
|
||||
<app-callout
|
||||
type="info"
|
||||
*ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'"
|
||||
>
|
||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="password-block">
|
||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||
<div class="generated-block" *ngIf="type === 'password'">
|
||||
<div class="generated-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'regeneratePassword' | i18n }}"
|
||||
(click)="regenerate()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="generated-block" *ngIf="type === 'username'">
|
||||
<div class="generated-wrapper" [innerHTML]="username | colorPassword" appSelectCopy></div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||
(click)="copy()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'regenerateUsername' | i18n }}"
|
||||
(click)="regenerate()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content condensed">
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="regenerate()">
|
||||
<i class="bwi bwi-fw bwi-generate" aria-hidden="true"></i>
|
||||
{{ "regeneratePassword" | i18n }}
|
||||
</a>
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="copy()">
|
||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i> {{ "copyPassword" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button type="button" (click)="toggleOptions()" [attr.aria-expanded]="showOptions">
|
||||
<i class="bwi bwi-plus-square" aria-hidden="true" [hidden]="showOptions"></i>
|
||||
<i class="bwi bwi-minus-square" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||
{{ "options" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="sr-only radio-header">{{ "type" | i18n }}</label>
|
||||
<label class="radio-header">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
name="PassTypeOptions"
|
||||
*ngFor="let o of passTypeOptions"
|
||||
name="TypeOptions"
|
||||
*ngFor="let o of typeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
[(ngModel)]="type"
|
||||
name="Type_{{ o.value }}"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
[checked]="passwordOptions.type === o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="type === o.value"
|
||||
[disabled]="showSelect"
|
||||
/>
|
||||
<label class="unstyled" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
@@ -58,148 +90,358 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions" *ngIf="passwordOptions.type === 'passphrase'">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||
<input
|
||||
id="num-words"
|
||||
type="number"
|
||||
min="3"
|
||||
max="20"
|
||||
(blur)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.numWords"
|
||||
/>
|
||||
<ng-container *ngIf="type === 'password'">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggleOptions()"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-expanded]="showOptions"
|
||||
>
|
||||
<i class="bwi bwi-plus-square" aria-hidden="true" [hidden]="showOptions"></i>
|
||||
<i class="bwi bwi-minus-square" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||
{{ "options" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||
<input
|
||||
id="word-separator"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
(input)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.wordSeparator"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||
<input
|
||||
id="capitalize"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.capitalize"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||
<input
|
||||
id="include-number"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.includeNumber"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||
/>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="radio-header">{{ "passwordType" | i18n }}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
name="PassTypeOptions"
|
||||
*ngFor="let o of passTypeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
name="PasswordType_{{ o.value }}"
|
||||
id="passwordType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
[checked]="passwordOptions.type === o.value"
|
||||
/>
|
||||
<label class="unstyled" for="passwordType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="passwordOptions.type === 'password'">
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box" [hidden]="!showOptions" *ngIf="passwordOptions.type === 'passphrase'">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||
<label for="length">{{ "length" | i18n }}</label>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||
<input
|
||||
id="length"
|
||||
id="num-words"
|
||||
type="number"
|
||||
min="5"
|
||||
max="128"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
min="3"
|
||||
max="20"
|
||||
(blur)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.numWords"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||
<input
|
||||
id="lengthRange"
|
||||
type="range"
|
||||
min="5"
|
||||
max="128"
|
||||
step="1"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
(change)="sliderChanged()"
|
||||
(input)="sliderInput()"
|
||||
id="word-separator"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
(input)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.wordSeparator"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="uppercase">A-Z</label>
|
||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||
<input
|
||||
id="uppercase"
|
||||
id="capitalize"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
[(ngModel)]="passwordOptions.capitalize"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||
<input
|
||||
id="lowercase"
|
||||
id="include-number"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input
|
||||
id="numbers"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
[(ngModel)]="passwordOptions.number"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input
|
||||
id="special"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
[(ngModel)]="passwordOptions.special"
|
||||
[(ngModel)]="passwordOptions.includeNumber"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<ng-container *ngIf="passwordOptions.type === 'password'">
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||
<label for="length">{{ "length" | i18n }}</label>
|
||||
<input
|
||||
id="length"
|
||||
type="number"
|
||||
min="5"
|
||||
max="128"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
(blur)="savePasswordOptions()"
|
||||
/>
|
||||
<input
|
||||
id="lengthRange"
|
||||
type="range"
|
||||
min="5"
|
||||
max="128"
|
||||
step="1"
|
||||
[(ngModel)]="passwordOptions.length"
|
||||
(change)="sliderChanged()"
|
||||
(input)="sliderInput()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="uppercase">A-Z</label>
|
||||
<input
|
||||
id="uppercase"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<input
|
||||
id="lowercase"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input
|
||||
id="numbers"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
[(ngModel)]="passwordOptions.number"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input
|
||||
id="special"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
[(ngModel)]="passwordOptions.special"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||
<input
|
||||
id="min-number"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(blur)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.minNumber"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||
<input
|
||||
id="min-special"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(blur)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.minSpecial"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="ambiguous">{{ "ambiguous" | i18n }}</label>
|
||||
<input
|
||||
id="ambiguous"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="avoidAmbiguous"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="type === 'username'">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggleOptions()"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
[attr.aria-expanded]="showOptions"
|
||||
>
|
||||
<i class="bwi bwi-plus-square" aria-hidden="true" [hidden]="showOptions"></i>
|
||||
<i class="bwi bwi-minus-square" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||
{{ "options" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="radio-header">{{ "usernameType" | i18n }}</label>
|
||||
<div
|
||||
class="radio-group align-start text-default"
|
||||
appBoxRow
|
||||
name="UsernameTypeOptions"
|
||||
*ngFor="let o of usernameTypeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="usernameOptions.type"
|
||||
name="UsernameType_{{ o.value }}"
|
||||
id="usernameType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.type === o.value"
|
||||
/>
|
||||
<label class="unstyled" for="usernameType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
<div class="small text-muted" *ngIf="o.desc">{{ o.desc }}</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'forwarded'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||
<div class="box-content-row">
|
||||
<label class="radio-header">{{ "service" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of forwardOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
name="ForwardType_{{ o.value }}"
|
||||
id="forwardtype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.forwardedService === o.value"
|
||||
/>
|
||||
<label for="forwardtype_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'subaddress'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="min-number"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(blur)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.minNumber"
|
||||
id="subaddress-email"
|
||||
type="text"
|
||||
name="SubaddressEmail"
|
||||
[(ngModel)]="usernameOptions.subaddressEmail"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||
<div class="box-content-row" *ngIf="subaddressOptions.length > 1">
|
||||
<label class="radio-header">{{ "type" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of subaddressOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.subaddressType"
|
||||
name="SubaddressType_{{ o.value }}"
|
||||
id="subaddresstype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.subaddressType === o.value"
|
||||
/>
|
||||
<label for="subaddresstype_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="showWebsiteOption">
|
||||
<label for="subaddress-website">{{ "website" | i18n }}</label>
|
||||
<input
|
||||
id="min-special"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(blur)="savePasswordOptions()"
|
||||
[(ngModel)]="passwordOptions.minSpecial"
|
||||
id="subaddress-website"
|
||||
type="text"
|
||||
name="SubaddressWebsite"
|
||||
[value]="usernameOptions.website"
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'catchall'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
|
||||
<input
|
||||
id="catchall-domain"
|
||||
type="text"
|
||||
name="CatchallDomain"
|
||||
[(ngModel)]="usernameOptions.catchallDomain"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="catchallOptions.length > 1">
|
||||
<label class="radio-header">{{ "type" | i18n }}</label>
|
||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of catchallOptions">
|
||||
<input
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.catchallType"
|
||||
name="CatchallType_{{ o.value }}"
|
||||
id="catchalltype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.catchallType === o.value"
|
||||
/>
|
||||
<label for="catchalltype_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="showWebsiteOption">
|
||||
<label for="catchall-website">{{ "website" | i18n }}</label>
|
||||
<input
|
||||
id="catchall-website"
|
||||
type="text"
|
||||
name="CatchallWebsite"
|
||||
[value]="usernameOptions.website"
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'word'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||
<input
|
||||
id="capitalize"
|
||||
type="checkbox"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordCapitalize"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="ambiguous">{{ "ambiguous" | i18n }}</label>
|
||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||
<input
|
||||
id="ambiguous"
|
||||
id="include-number"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="avoidAmbiguous"
|
||||
(change)="saveUsernameOptions()"
|
||||
[(ngModel)]="usernameOptions.wordIncludeNumber"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
(onCancelled)="cancelledAddEdit($event)"
|
||||
(onShareCipher)="shareCipher($event)"
|
||||
(onEditCollections)="cipherCollections($event)"
|
||||
(onGeneratePassword)="openPasswordGenerator(true)"
|
||||
(onGeneratePassword)="openGenerator(true, true)"
|
||||
(onGenerateUsername)="openGenerator(true, false)"
|
||||
>
|
||||
</app-vault-add-edit>
|
||||
<div
|
||||
|
||||
@@ -120,8 +120,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
(document.querySelector("#search") as HTMLInputElement).select();
|
||||
detectChanges = false;
|
||||
break;
|
||||
case "openPasswordGenerator":
|
||||
await this.openPasswordGenerator(false);
|
||||
case "openGenerator":
|
||||
await this.openGenerator(false);
|
||||
break;
|
||||
case "syncCompleted":
|
||||
await this.load();
|
||||
@@ -599,28 +599,39 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
this.go();
|
||||
}
|
||||
|
||||
async openPasswordGenerator(showSelect: boolean) {
|
||||
async openGenerator(showSelect: boolean, passwordType = true) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const cipher = this.addEditComponent?.cipher;
|
||||
const loginType = cipher != null && cipher.type === CipherType.Login && cipher.login != null;
|
||||
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
PasswordGeneratorComponent,
|
||||
this.passwordGeneratorModalRef,
|
||||
(comp) => (comp.showSelect = showSelect)
|
||||
(comp) => {
|
||||
comp.showSelect = showSelect;
|
||||
if (showSelect) {
|
||||
comp.type = passwordType ? "password" : "username";
|
||||
if (loginType && cipher.login.hasUris && cipher.login.uris[0].hostname != null) {
|
||||
comp.usernameWebsite = cipher.login.uris[0].hostname;
|
||||
comp.showWebsiteOption = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
this.modal = modal;
|
||||
|
||||
childComponent.onSelected.subscribe((password: string) => {
|
||||
childComponent.onSelected.subscribe((value: string) => {
|
||||
this.modal.close();
|
||||
if (
|
||||
this.addEditComponent != null &&
|
||||
this.addEditComponent.cipher != null &&
|
||||
this.addEditComponent.cipher.type === CipherType.Login &&
|
||||
this.addEditComponent.cipher.login != null
|
||||
) {
|
||||
if (loginType) {
|
||||
this.addEditComponent.markPasswordAsDirty();
|
||||
this.addEditComponent.cipher.login.password = password;
|
||||
if (passwordType) {
|
||||
this.addEditComponent.cipher.login.password = value;
|
||||
} else {
|
||||
this.addEditComponent.cipher.login.username = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
<div
|
||||
*ngIf="showPassword"
|
||||
class="monospaced password-wrapper"
|
||||
class="monospaced generated-wrapper"
|
||||
appSelectCopy
|
||||
[innerHTML]="cipher.login.password | colorPassword"
|
||||
></div>
|
||||
|
||||
Reference in New Issue
Block a user