add username generation to generator (#1566)

* add username generation to generator

* move bottom buttons into existing containers
This commit is contained in:
Kyle Spearrin 2022-03-29 15:02:48 -04:00 committed by GitHub
parent 42ececbcf5
commit 23b02a770a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 338 additions and 163 deletions

View File

@ -240,7 +240,7 @@ const routes: Routes = [
{ {
path: "generator", path: "generator",
component: PasswordGeneratorComponent, component: PasswordGeneratorComponent,
data: { titleId: "passwordGenerator" }, data: { titleId: "generator" },
}, },
{ {
path: "breach-report", path: "breach-report",

View File

@ -17,7 +17,7 @@
<li class="list-group-item d-flex" *ngFor="let h of history"> <li class="list-group-item d-flex" *ngFor="let h of history">
<div class="password-row"> <div class="password-row">
<div <div
class="text-monospace password-wrapper" class="text-monospace generated-wrapper"
[innerHTML]="h.password | colorPassword" [innerHTML]="h.password | colorPassword"
appSelectCopy appSelectCopy
></div> ></div>

View File

@ -1,27 +1,58 @@
<div class="page-header"> <div class="page-header">
<h1>{{ "passwordGenerator" | i18n }}</h1> <h1>{{ "generator" | i18n }}</h1>
</div> </div>
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect()"> <app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'">
{{ "passwordGeneratorPolicyInEffect" | i18n }} {{ "passwordGeneratorPolicyInEffect" | i18n }}
</app-callout> </app-callout>
<div class="card card-password bg-light my-4"> <div class="card card-generated bg-light my-4">
<div class="card-body"> <div class="card-body">
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div> <div
*ngIf="type === 'password'"
class="generated-wrapper"
[innerHTML]="password | colorPassword"
appSelectCopy
></div>
<div
*ngIf="type === 'username'"
class="generated-wrapper"
[innerHTML]="username | colorPassword"
appSelectCopy
></div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="d-block">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
<div class="form-check form-check-inline" *ngFor="let o of typeOptions">
<input
class="form-check-input"
type="radio"
[(ngModel)]="type"
name="Type_{{ o.value }}"
id="type_{{ o.value }}"
[value]="o.value"
(change)="typeChanged()"
[checked]="type === o.value"
/>
<label class="form-check-label" for="type_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
<ng-container *ngIf="type === 'password'">
<div class="form-group">
<label class="d-block">{{ "passwordType" | i18n }}</label>
<div class="form-check form-check-inline" *ngFor="let o of passTypeOptions"> <div class="form-check form-check-inline" *ngFor="let o of passTypeOptions">
<input <input
class="form-check-input" class="form-check-input"
type="radio" type="radio"
[(ngModel)]="passwordOptions.type" [(ngModel)]="passwordOptions.type"
name="Type_{{ o.value }}" name="PasswordType_{{ o.value }}"
id="type_{{ o.value }}" id="passwordType_{{ o.value }}"
[value]="o.value" [value]="o.value"
(change)="savePasswordOptions()" (change)="savePasswordOptions()"
[checked]="passwordOptions.type === o.value" [checked]="passwordOptions.type === o.value"
/> />
<label class="form-check-label" for="type_{{ o.value }}"> <label class="form-check-label" for="passwordType_{{ o.value }}">
{{ o.name }} {{ o.name }}
</label> </label>
</div> </div>
@ -52,6 +83,7 @@
/> />
</div> </div>
</div> </div>
<label class="d-block">{{ "options" | i18n }}</label>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input <input
@ -119,6 +151,7 @@
/> />
</div> </div>
</div> </div>
<label class="d-block">{{ "options" | i18n }}</label>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input <input
@ -196,4 +229,102 @@
</button> </button>
</div> </div>
</div> </div>
</ng-container>
<ng-container *ngIf="type === 'username'">
<div class="form-group">
<label class="d-block">{{ "usernameType" | i18n }}</label>
<div class="form-check" *ngFor="let o of usernameTypeOptions">
<input
class="form-check-input"
type="radio"
[(ngModel)]="usernameOptions.type"
name="UsernameType_{{ o.value }}"
id="usernameType_{{ o.value }}"
[value]="o.value"
(change)="saveUsernameOptions()"
[checked]="usernameOptions.type === o.value"
/>
<label class="form-check-label" for="usernameType_{{ o.value }}">
{{ o.name }}
<div class="small text-muted">{{ o.desc }}</div>
</label>
</div>
</div>
<div class="form-group" *ngIf="usernameOptions.type === 'forwarded'">
<div class="form-check form-check-inline" *ngFor="let o of forwardOptions">
<input
class="form-check-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 class="form-check-label" for="forwardtype_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
<div class="row" *ngIf="usernameOptions.type === 'subaddress'">
<div class="form-group col-4">
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
<input
id="subaddress-email"
class="form-control"
type="text"
[(ngModel)]="usernameOptions.subaddressEmail"
(blur)="saveUsernameOptions()"
/>
</div>
</div>
<div class="row" *ngIf="usernameOptions.type === 'catchall'">
<div class="form-group col-4">
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
<input
id="catchall-domain"
class="form-control"
type="text"
[(ngModel)]="usernameOptions.catchallDomain"
(blur)="saveUsernameOptions()"
/>
</div>
</div>
<ng-container *ngIf="usernameOptions.type === 'word'">
<label class="d-block">{{ "options" | i18n }}</label>
<div class="row">
<div class="form-group">
<div class="form-check">
<input
id="capitalizeUsername"
type="checkbox"
(change)="saveUsernameOptions()"
[(ngModel)]="usernameOptions.wordCapitalize"
/>
<label for="capitalizeUsername" class="form-check-label">{{ "capitalize" | i18n }}</label>
</div>
<div class="form-check">
<input
id="includeNumberUsername"
type="checkbox"
(change)="saveUsernameOptions()"
[(ngModel)]="usernameOptions.wordIncludeNumber"
/>
<label for="includeNumberUsername" class="form-check-label">{{
"includeNumber" | i18n
}}</label>
</div>
</div>
</div>
</ng-container>
<div>
<button type="button" class="btn btn-primary" (click)="regenerate()">
{{ "regenerateUsername" | i18n }}
</button>
<button type="button" class="btn btn-outline-secondary" (click)="copy()">
{{ "copyUsername" | i18n }}
</button>
</div>
</ng-container>
<ng-template #historyTemplate></ng-template> <ng-template #historyTemplate></ng-template>

View File

@ -5,7 +5,7 @@
<div class="card-header">{{ "tools" | i18n }}</div> <div class="card-header">{{ "tools" | i18n }}</div>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<a routerLink="generator" class="list-group-item" routerLinkActive="active"> <a routerLink="generator" class="list-group-item" routerLinkActive="active">
{{ "passwordGenerator" | i18n }} {{ "generator" | i18n }}
</a> </a>
<a routerLink="import" class="list-group-item" routerLinkActive="active"> <a routerLink="import" class="list-group-item" routerLinkActive="active">
{{ "importData" | i18n }} {{ "importData" | i18n }}

View File

@ -769,7 +769,7 @@
<div class="ml-3" *ngIf="viewingPasswordHistory"> <div class="ml-3" *ngIf="viewingPasswordHistory">
<div *ngFor="let ph of cipher.passwordHistory"> <div *ngFor="let ph of cipher.passwordHistory">
{{ ph.lastUsedDate | date: "short" }} - {{ ph.lastUsedDate | date: "short" }} -
<span class="password-wrapper text-monospace ml-2">{{ ph.password }}</span> <span class="generated-wrapper text-monospace ml-2">{{ ph.password }}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -187,7 +187,12 @@
"message": "Edit Folder" "message": "Edit Folder"
}, },
"baseDomain": { "baseDomain": {
"message": "Base domain" "message": "Base domain",
"description": "Domain name. Ex. website.com"
},
"domainName": {
"message": "Domain Name",
"description": "Domain name. Ex. website.com"
}, },
"host": { "host": {
"message": "Host", "message": "Host",
@ -4826,5 +4831,44 @@
"example": "My Org Name" "example": "My Org Name"
} }
} }
},
"generator": {
"message": "Generator"
},
"whatWouldYouLikeToGenerate": {
"message": "What would you like to generate?"
},
"passwordType": {
"message": "Password Type"
},
"regenerateUsername": {
"message": "Regenerate Username"
},
"generateUsername": {
"message": "Generate Username"
},
"usernameType": {
"message": "Username Type"
},
"plusAddressedEmail": {
"message": "Plus Addressed Email"
},
"plusAddressedEmailDesc": {
"message": "Use your email provider's sub-addressing capabilities."
},
"catchallEmail": {
"message": "Catch-all Email"
},
"catchallEmailDesc": {
"message": "Use your domain's configured catch-all inbox."
},
"random": {
"message": "Random"
},
"randomWord": {
"message": "Random Word"
},
"service": {
"message": "Service"
} }
} }

View File

@ -1,4 +1,4 @@
.password-wrapper { .generated-wrapper {
min-width: 0; min-width: 0;
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-all; word-break: break-all;
@ -114,7 +114,7 @@ app-password-generator {
width: 100%; width: 100%;
} }
.card-password { .card-generated {
.card-body { .card-body {
@include themify($themes) { @include themify($themes) {
background: themed("foregroundColor"); background: themed("foregroundColor");