mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-22 16:29:09 +01:00
Do not redirect after saving changes to excluded domains (#11676)
This commit is contained in:
parent
b3b311e164
commit
15c301d39f
@ -11,7 +11,7 @@
|
|||||||
accountSwitcherEnabled ? ("excludedDomainsDescAlt" | i18n) : ("excludedDomainsDesc" | i18n)
|
accountSwitcherEnabled ? ("excludedDomainsDescAlt" | i18n) : ("excludedDomainsDesc" | i18n)
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
<bit-section>
|
<bit-section *ngIf="!isLoading">
|
||||||
<bit-section-header>
|
<bit-section-header>
|
||||||
<h2 bitTypography="h6">{{ "domainsTitle" | i18n }}</h2>
|
<h2 bitTypography="h6">{{ "domainsTitle" | i18n }}</h2>
|
||||||
<span bitTypography="body2" slot="end">{{ excludedDomainsState?.length || 0 }}</span>
|
<span bitTypography="body2" slot="end">{{ excludedDomainsState?.length || 0 }}</span>
|
||||||
@ -57,7 +57,13 @@
|
|||||||
</bit-section>
|
</bit-section>
|
||||||
</div>
|
</div>
|
||||||
<popup-footer slot="footer">
|
<popup-footer slot="footer">
|
||||||
<button bitButton buttonType="primary" type="submit" (click)="saveChanges()">
|
<button
|
||||||
|
bitButton
|
||||||
|
buttonType="primary"
|
||||||
|
type="submit"
|
||||||
|
[disabled]="dataIsPristine"
|
||||||
|
(click)="saveChanges()"
|
||||||
|
>
|
||||||
{{ "save" | i18n }}
|
{{ "save" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</popup-footer>
|
</popup-footer>
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { QueryList, Component, ElementRef, OnDestroy, OnInit, ViewChildren } from "@angular/core";
|
import {
|
||||||
|
QueryList,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
OnDestroy,
|
||||||
|
AfterViewInit,
|
||||||
|
ViewChildren,
|
||||||
|
} from "@angular/core";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
import { Router, RouterModule } from "@angular/router";
|
import { RouterModule } from "@angular/router";
|
||||||
import { firstValueFrom, Subject, takeUntil } from "rxjs";
|
import { Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
@ -29,8 +35,6 @@ import { PopupFooterComponent } from "../../../platform/popup/layout/popup-foote
|
|||||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = "excludedDomainsState";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-excluded-domains",
|
selector: "app-excluded-domains",
|
||||||
templateUrl: "excluded-domains.component.html",
|
templateUrl: "excluded-domains.component.html",
|
||||||
@ -55,11 +59,12 @@ const BroadcasterSubscriptionId = "excludedDomainsState";
|
|||||||
TypographyModule,
|
TypographyModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
|
||||||
@ViewChildren("uriInput") uriInputElements: QueryList<ElementRef<HTMLInputElement>>;
|
@ViewChildren("uriInput") uriInputElements: QueryList<ElementRef<HTMLInputElement>>;
|
||||||
|
|
||||||
accountSwitcherEnabled = false;
|
accountSwitcherEnabled = false;
|
||||||
dataIsPristine = true;
|
dataIsPristine = true;
|
||||||
|
isLoading = false;
|
||||||
excludedDomainsState: string[] = [];
|
excludedDomainsState: string[] = [];
|
||||||
storedExcludedDomains: string[] = [];
|
storedExcludedDomains: string[] = [];
|
||||||
// How many fields should be non-editable before editable fields
|
// How many fields should be non-editable before editable fields
|
||||||
@ -70,16 +75,27 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private domainSettingsService: DomainSettingsService,
|
private domainSettingsService: DomainSettingsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private router: Router,
|
|
||||||
private broadcasterService: BroadcasterService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
) {
|
) {
|
||||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
this.accountSwitcherEnabled = enableAccountSwitching();
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngAfterViewInit() {
|
||||||
const neverDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
|
this.domainSettingsService.neverDomains$
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe((neverDomains: NeverDomains) => this.handleStateUpdate(neverDomains));
|
||||||
|
|
||||||
|
this.uriInputElements.changes.pipe(takeUntil(this.destroy$)).subscribe(({ last }) => {
|
||||||
|
this.focusNewUriInput(last);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStateUpdate(neverDomains: NeverDomains) {
|
||||||
if (neverDomains) {
|
if (neverDomains) {
|
||||||
this.storedExcludedDomains = Object.keys(neverDomains);
|
this.storedExcludedDomains = Object.keys(neverDomains);
|
||||||
}
|
}
|
||||||
@ -89,15 +105,8 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
// Do not allow the first x (pre-existing) fields to be edited
|
// Do not allow the first x (pre-existing) fields to be edited
|
||||||
this.fieldsEditThreshold = this.storedExcludedDomains.length;
|
this.fieldsEditThreshold = this.storedExcludedDomains.length;
|
||||||
|
|
||||||
this.uriInputElements.changes.pipe(takeUntil(this.destroy$)).subscribe(({ last }) => {
|
this.dataIsPristine = true;
|
||||||
this.focusNewUriInput(last);
|
this.isLoading = false;
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
|
||||||
this.destroy$.next();
|
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
focusNewUriInput(elementRef: ElementRef) {
|
focusNewUriInput(elementRef: ElementRef) {
|
||||||
@ -116,7 +125,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
async removeDomain(i: number) {
|
async removeDomain(i: number) {
|
||||||
this.excludedDomainsState.splice(i, 1);
|
this.excludedDomainsState.splice(i, 1);
|
||||||
|
|
||||||
// if a pre-existing field was dropped, lower the edit threshold
|
// If a pre-existing field was dropped, lower the edit threshold
|
||||||
if (i < this.fieldsEditThreshold) {
|
if (i < this.fieldsEditThreshold) {
|
||||||
this.fieldsEditThreshold--;
|
this.fieldsEditThreshold--;
|
||||||
}
|
}
|
||||||
@ -132,11 +141,11 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async saveChanges() {
|
async saveChanges() {
|
||||||
if (this.dataIsPristine) {
|
if (this.dataIsPristine) {
|
||||||
await this.router.navigate(["/notifications"]);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
const newExcludedDomainsSaveState: NeverDomains = {};
|
const newExcludedDomainsSaveState: NeverDomains = {};
|
||||||
const uniqueExcludedDomains = new Set(this.excludedDomainsState);
|
const uniqueExcludedDomains = new Set(this.excludedDomainsState);
|
||||||
|
|
||||||
@ -151,6 +160,8 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
this.i18nService.t("excludedDomainsInvalidDomain", uri),
|
this.i18nService.t("excludedDomainsInvalidDomain", uri),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Don't reset via `handleStateUpdate` to allow existing input value correction
|
||||||
|
this.isLoading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +170,23 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const existingState = new Set(this.storedExcludedDomains);
|
||||||
|
const newState = new Set(Object.keys(newExcludedDomainsSaveState));
|
||||||
|
const stateIsUnchanged =
|
||||||
|
existingState.size === newState.size &&
|
||||||
|
new Set([...existingState, ...newState]).size === existingState.size;
|
||||||
|
|
||||||
|
// The subscriber updates don't trigger if `setNeverDomains` sets an equivalent state
|
||||||
|
if (stateIsUnchanged) {
|
||||||
|
// Reset UI state directly
|
||||||
|
const constructedNeverDomainsState = this.storedExcludedDomains.reduce(
|
||||||
|
(neverDomains, uri) => ({ ...neverDomains, [uri]: null }),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
this.handleStateUpdate(constructedNeverDomainsState);
|
||||||
|
} else {
|
||||||
await this.domainSettingsService.setNeverDomains(newExcludedDomainsSaveState);
|
await this.domainSettingsService.setNeverDomains(newExcludedDomainsSaveState);
|
||||||
|
}
|
||||||
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"success",
|
"success",
|
||||||
@ -169,11 +196,9 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
|||||||
} catch {
|
} catch {
|
||||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError"));
|
this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError"));
|
||||||
|
|
||||||
// Do not navigate on error
|
// Don't reset via `handleStateUpdate` to preserve input values
|
||||||
return;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.router.navigate(["/notifications"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByFunction(index: number, _: string) {
|
trackByFunction(index: number, _: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user