mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PS-713] Fix locale search bug (#3014)
* [PS-713] Fix locale search bug * [PS-713] Add new locales to start at 1 char search * [PS-713] Switch to ReplaySubject and other edits from PR comments * PS-713: Add destroy to other sub and make locale inline a const * PS-713: Use firstValueFrom instead of takeUntil * PS-713: get this.locale asynchronously Co-authored-by: Colton Hurst <churst@bitwarden.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { DomSanitizer } from "@angular/platform-browser";
|
|||||||
import { NavigationEnd, Router } from "@angular/router";
|
import { NavigationEnd, Router } from "@angular/router";
|
||||||
import * as jq from "jquery";
|
import * as jq from "jquery";
|
||||||
import { IndividualConfig, ToastrService } from "ngx-toastr";
|
import { IndividualConfig, ToastrService } from "ngx-toastr";
|
||||||
|
import { Subject, takeUntil } from "rxjs";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
@@ -48,6 +49,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
private lastActivity: number = null;
|
private lastActivity: number = null;
|
||||||
private idleTimer: number = null;
|
private idleTimer: number = null;
|
||||||
private isIdle = false;
|
private isIdle = false;
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
@@ -78,7 +80,9 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.document.documentElement.lang = this.i18nService.locale;
|
this.i18nService.locale$.pipe(takeUntil(this.destroy$)).subscribe((locale) => {
|
||||||
|
this.document.documentElement.lang = locale;
|
||||||
|
});
|
||||||
|
|
||||||
this.ngZone.runOutsideAngular(() => {
|
this.ngZone.runOutsideAngular(() => {
|
||||||
window.onmousemove = () => this.recordActivity();
|
window.onmousemove = () => this.recordActivity();
|
||||||
@@ -181,7 +185,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.router.events.subscribe((event) => {
|
this.router.events.pipe(takeUntil(this.destroy$)).subscribe((event) => {
|
||||||
if (event instanceof NavigationEnd) {
|
if (event instanceof NavigationEnd) {
|
||||||
const modals = Array.from(document.querySelectorAll(".modal"));
|
const modals = Array.from(document.querySelectorAll(".modal"));
|
||||||
for (const modal of modals) {
|
for (const modal of modals) {
|
||||||
@@ -211,6 +215,8 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async logOut(expired: boolean) {
|
private async logOut(expired: boolean) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { formatDate } from "@angular/common";
|
import { formatDate } from "@angular/common";
|
||||||
import { Component, EventEmitter, Input, Output, OnInit } from "@angular/core";
|
import { Component, EventEmitter, Input, Output, OnInit } from "@angular/core";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
@@ -23,6 +24,8 @@ export class SponsoringOrgRowComponent implements OnInit {
|
|||||||
revokeSponsorshipPromise: Promise<any>;
|
revokeSponsorshipPromise: Promise<any>;
|
||||||
resendEmailPromise: Promise<any>;
|
resendEmailPromise: Promise<any>;
|
||||||
|
|
||||||
|
private locale = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
@@ -30,7 +33,9 @@ export class SponsoringOrgRowComponent implements OnInit {
|
|||||||
private platformUtilsService: PlatformUtilsService
|
private platformUtilsService: PlatformUtilsService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
async ngOnInit() {
|
||||||
|
this.locale = await firstValueFrom(this.i18nService.locale$);
|
||||||
|
|
||||||
this.setStatus(
|
this.setStatus(
|
||||||
this.isSelfHosted,
|
this.isSelfHosted,
|
||||||
this.sponsoringOrg.familySponsorshipToDelete,
|
this.sponsoringOrg.familySponsorshipToDelete,
|
||||||
@@ -98,7 +103,7 @@ export class SponsoringOrgRowComponent implements OnInit {
|
|||||||
// They want to delete but there is a valid until date which means there is an active sponsorship
|
// They want to delete but there is a valid until date which means there is an active sponsorship
|
||||||
this.statusMessage = this.i18nService.t(
|
this.statusMessage = this.i18nService.t(
|
||||||
"revokeWhenExpired",
|
"revokeWhenExpired",
|
||||||
formatDate(validUntil, "MM/dd/yyyy", this.i18nService.locale)
|
formatDate(validUntil, "MM/dd/yyyy", this.locale)
|
||||||
);
|
);
|
||||||
this.statusClass = "text-danger";
|
this.statusClass = "text-danger";
|
||||||
} else if (toDelete) {
|
} else if (toDelete) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
export abstract class I18nService {
|
export abstract class I18nService {
|
||||||
locale: string;
|
locale$: Observable<string>;
|
||||||
supportedTranslationLocales: string[];
|
supportedTranslationLocales: string[];
|
||||||
translationLocale: string;
|
translationLocale: string;
|
||||||
collator: Intl.Collator;
|
collator: Intl.Collator;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import { Observable, ReplaySubject } from "rxjs";
|
||||||
|
|
||||||
import { I18nService as I18nServiceAbstraction } from "../abstractions/i18n.service";
|
import { I18nService as I18nServiceAbstraction } from "../abstractions/i18n.service";
|
||||||
|
|
||||||
export class I18nService implements I18nServiceAbstraction {
|
export class I18nService implements I18nServiceAbstraction {
|
||||||
locale: string;
|
private _locale = new ReplaySubject<string>(1);
|
||||||
|
locale$: Observable<string> = this._locale.asObservable();
|
||||||
// First locale is the default (English)
|
// First locale is the default (English)
|
||||||
supportedTranslationLocales: string[] = ["en"];
|
supportedTranslationLocales: string[] = ["en"];
|
||||||
translationLocale: string;
|
translationLocale: string;
|
||||||
@@ -85,10 +88,14 @@ export class I18nService implements I18nServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.inited = true;
|
this.inited = true;
|
||||||
this.locale = this.translationLocale = locale != null ? locale : this.systemLanguage;
|
this.translationLocale = locale != null ? locale : this.systemLanguage;
|
||||||
|
this._locale.next(this.translationLocale);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.collator = new Intl.Collator(this.locale, { numeric: true, sensitivity: "base" });
|
this.collator = new Intl.Collator(this.translationLocale, {
|
||||||
|
numeric: true,
|
||||||
|
sensitivity: "base",
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
this.collator = null;
|
this.collator = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,23 @@ export class SearchService implements SearchServiceAbstraction {
|
|||||||
indexedEntityId?: string = null;
|
indexedEntityId?: string = null;
|
||||||
private indexing = false;
|
private indexing = false;
|
||||||
private index: lunr.Index = null;
|
private index: lunr.Index = null;
|
||||||
private searchableMinLength = 2;
|
private readonly immediateSearchLocales: string[] = ["zh-CN", "zh-TW", "ja", "ko", "vi"];
|
||||||
|
private readonly defaultSearchableMinLength: number = 2;
|
||||||
|
private searchableMinLength: number = this.defaultSearchableMinLength;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private i18nService: I18nService
|
private i18nService: I18nService
|
||||||
) {
|
) {
|
||||||
if (["zh-CN", "zh-TW"].indexOf(i18nService.locale) !== -1) {
|
this.i18nService.locale$.subscribe((locale) => {
|
||||||
this.searchableMinLength = 1;
|
if (this.immediateSearchLocales.indexOf(locale) !== -1) {
|
||||||
}
|
this.searchableMinLength = 1;
|
||||||
|
} else {
|
||||||
|
this.searchableMinLength = this.defaultSearchableMinLength;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//register lunr pipeline function
|
//register lunr pipeline function
|
||||||
lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents");
|
lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
|
|
||||||
export class I18nMockService implements I18nService {
|
export class I18nMockService implements I18nService {
|
||||||
locale: string;
|
locale$: Observable<string>;
|
||||||
supportedTranslationLocales: string[];
|
supportedTranslationLocales: string[];
|
||||||
translationLocale: string;
|
translationLocale: string;
|
||||||
collator: Intl.Collator;
|
collator: Intl.Collator;
|
||||||
|
|||||||
Reference in New Issue
Block a user