From 7af9055ce76e8cfda582e61a78f9398153273b43 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Wed, 3 Nov 2021 09:48:28 -0700 Subject: [PATCH 1/3] feat: table prefs now use sessionStorage instead of localStorage --- .../src/preference/preference.service.ts | 40 ++++++++++++++++--- .../browser/storage/session-storage.ts | 9 +++++ .../multi-select/multi-select.component.ts | 3 +- .../table/table-widget-renderer.component.ts | 13 +++--- 4 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 projects/common/src/utilities/browser/storage/session-storage.ts diff --git a/projects/common/src/preference/preference.service.ts b/projects/common/src/preference/preference.service.ts index 198ab646a..b2c585e5b 100644 --- a/projects/common/src/preference/preference.service.ts +++ b/projects/common/src/preference/preference.service.ts @@ -1,21 +1,33 @@ import { Injectable } from '@angular/core'; import { Observable, of, throwError } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; +import { AbstractStorage } from '../utilities/browser/storage/abstract-storage'; import { LocalStorage } from '../utilities/browser/storage/local-storage'; +import { SessionStorage } from '../utilities/browser/storage/session-storage'; import { BooleanCoercer } from '../utilities/coercers/boolean-coercer'; import { NumberCoercer } from '../utilities/coercers/number-coercer'; +export const enum StorageType { + Local = 'local', + Session = 'session' +} + @Injectable({ providedIn: 'root' }) export class PreferenceService { + private static readonly DEFAULT_STORAGE_TYPE: StorageType = StorageType.Local; + private static readonly PREFERENCE_STORAGE_NAMESPACE: string = 'preference'; private static readonly SEPARATOR_CHAR: string = '.'; private static readonly SEPARATOR_REGEX: RegExp = /\.(.+)/; private readonly numberCoercer: NumberCoercer = new NumberCoercer(); private readonly booleanCoercer: BooleanCoercer = new BooleanCoercer(); - public constructor(private readonly preferenceStorage: LocalStorage) {} + public constructor( + private readonly localStorage: LocalStorage, + private readonly sessionStorage: SessionStorage + ) {} /** * Returns the current storage value if defined, else the default value. The observable @@ -23,8 +35,12 @@ export class PreferenceService { * preference becomes unset. If default value is not provided, the observable will * throw in the case the preference is unset. */ - public get(key: PreferenceKey, defaultValue?: T): Observable { - return this.preferenceStorage.watch(this.asStorageKey(key)).pipe( + public get( + key: PreferenceKey, + defaultValue?: T, + type: StorageType = PreferenceService.DEFAULT_STORAGE_TYPE + ): Observable { + return this.preferenceStorage(type).watch(this.asStorageKey(key)).pipe( map(storedValue => this.fromStorageValue(storedValue) ?? defaultValue), switchMap(value => value === undefined @@ -34,9 +50,13 @@ export class PreferenceService { ); } - public set(key: PreferenceKey, value: PreferenceValue): void { + public set( + key: PreferenceKey, + value: PreferenceValue, + type: StorageType = PreferenceService.DEFAULT_STORAGE_TYPE + ): void { const val = this.asStorageValue(value); - this.preferenceStorage.set(this.asStorageKey(key), val); + this.preferenceStorage(type).set(this.asStorageKey(key), val); } private asStorageKey(key: PreferenceKey): PreferenceStorageKey { @@ -70,6 +90,16 @@ export class PreferenceService { return undefined; } } + + private preferenceStorage(type: StorageType): AbstractStorage { + switch (type) { + case StorageType.Session: + return this.sessionStorage; + case StorageType.Local: + default: + return this.localStorage; + } + } } type PreferenceStorageKey = string; diff --git a/projects/common/src/utilities/browser/storage/session-storage.ts b/projects/common/src/utilities/browser/storage/session-storage.ts new file mode 100644 index 000000000..da1f9d1c9 --- /dev/null +++ b/projects/common/src/utilities/browser/storage/session-storage.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; +import { AbstractStorage } from './abstract-storage'; + +@Injectable({ providedIn: 'root' }) +export class SessionStorage extends AbstractStorage { + public constructor() { + super(sessionStorage); + } +} diff --git a/projects/components/src/multi-select/multi-select.component.ts b/projects/components/src/multi-select/multi-select.component.ts index 307d0bcb7..efa296a07 100644 --- a/projects/components/src/multi-select/multi-select.component.ts +++ b/projects/components/src/multi-select/multi-select.component.ts @@ -10,7 +10,7 @@ import { QueryList } from '@angular/core'; import { IconType } from '@hypertrace/assets-library'; -import { queryListAndChanges$, SubscriptionLifecycle } from '@hypertrace/common'; +import { queryListAndChanges$ } from '@hypertrace/common'; import { BehaviorSubject, combineLatest, EMPTY, Observable, of, Subject } from 'rxjs'; import { map } from 'rxjs/operators'; import { ButtonRole, ButtonStyle } from '../button/button'; @@ -24,7 +24,6 @@ import { MultiSelectJustify } from './multi-select-justify'; selector: 'ht-multi-select', styleUrls: ['./multi-select.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - providers: [SubscriptionLifecycle], template: `
{ return isNonEmptyString(this.model.viewId) - ? this.preferenceService.get(this.model.viewId, {}).pipe(first()) + ? this.preferenceService.get(this.model.viewId, {}, StorageType.Session).pipe(first()) : of({}); } private setViewPreferences(preferences: TableWidgetViewPreferences): void { if (isNonEmptyString(this.model.viewId)) { - this.preferenceService.set(this.model.viewId, preferences); + this.preferenceService.set(this.model.viewId, preferences, StorageType.Session); } } @@ -562,13 +563,13 @@ export class TableWidgetRendererComponent defaultPreferences: TableWidgetPreferences = TableWidgetRendererComponent.DEFAULT_PREFERENCES ): Observable { return isNonEmptyString(this.model.getId()) - ? this.preferenceService.get(this.model.getId()!, defaultPreferences).pipe(first()) + ? this.preferenceService.get(this.model.getId()!, defaultPreferences, StorageType.Session).pipe(first()) : of(defaultPreferences); } private setPreferences(preferences: TableWidgetPreferences): void { if (isNonEmptyString(this.model.getId())) { - this.preferenceService.set(this.model.getId()!, preferences); + this.preferenceService.set(this.model.getId()!, preferences, StorageType.Session); } } From 9cc7ce34be0ffa93d9d3316bd09228315a55c77b Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Wed, 3 Nov 2021 10:20:33 -0700 Subject: [PATCH 2/3] style: prettier --- .../src/preference/preference.service.ts | 23 +++++++++---------- .../table/table-widget-renderer.component.ts | 4 +++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/projects/common/src/preference/preference.service.ts b/projects/common/src/preference/preference.service.ts index b2c585e5b..3e92c76e6 100644 --- a/projects/common/src/preference/preference.service.ts +++ b/projects/common/src/preference/preference.service.ts @@ -24,10 +24,7 @@ export class PreferenceService { private readonly numberCoercer: NumberCoercer = new NumberCoercer(); private readonly booleanCoercer: BooleanCoercer = new BooleanCoercer(); - public constructor( - private readonly localStorage: LocalStorage, - private readonly sessionStorage: SessionStorage - ) {} + public constructor(private readonly localStorage: LocalStorage, private readonly sessionStorage: SessionStorage) {} /** * Returns the current storage value if defined, else the default value. The observable @@ -40,14 +37,16 @@ export class PreferenceService { defaultValue?: T, type: StorageType = PreferenceService.DEFAULT_STORAGE_TYPE ): Observable { - return this.preferenceStorage(type).watch(this.asStorageKey(key)).pipe( - map(storedValue => this.fromStorageValue(storedValue) ?? defaultValue), - switchMap(value => - value === undefined - ? throwError(Error(`No value found or default provided for preferenceKey: ${key}`)) - : of(value) - ) - ); + return this.preferenceStorage(type) + .watch(this.asStorageKey(key)) + .pipe( + map(storedValue => this.fromStorageValue(storedValue) ?? defaultValue), + switchMap(value => + value === undefined + ? throwError(Error(`No value found or default provided for preferenceKey: ${key}`)) + : of(value) + ) + ); } public set( diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts index c9d3d1ff5..8012bda38 100644 --- a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts +++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts @@ -563,7 +563,9 @@ export class TableWidgetRendererComponent defaultPreferences: TableWidgetPreferences = TableWidgetRendererComponent.DEFAULT_PREFERENCES ): Observable { return isNonEmptyString(this.model.getId()) - ? this.preferenceService.get(this.model.getId()!, defaultPreferences, StorageType.Session).pipe(first()) + ? this.preferenceService + .get(this.model.getId()!, defaultPreferences, StorageType.Session) + .pipe(first()) : of(defaultPreferences); } From 24a0f85a10413ea3e7ca2364622b3cd26661dabb Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Wed, 3 Nov 2021 10:31:54 -0700 Subject: [PATCH 3/3] style: lint imports --- .../dashboard/widgets/table/table-widget-renderer.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts index 8012bda38..4139389ec 100644 --- a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts +++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts @@ -32,7 +32,7 @@ import { } from '@hypertrace/components'; import { WidgetRenderer } from '@hypertrace/dashboards'; import { Renderer } from '@hypertrace/hyperdash'; -import { RENDERER_API, RendererApi } from '@hypertrace/hyperdash-angular'; +import { RendererApi, RENDERER_API } from '@hypertrace/hyperdash-angular'; import { capitalize, isEmpty, isEqual, pick } from 'lodash-es'; import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs'; import {