Browse Source

UI: Use user settings service.

pull/8247/head
Igor Kulikov 4 years ago
parent
commit
290e15ecb8
  1. 8
      ui-ngx/src/app/core/auth/auth.effects.ts
  2. 4
      ui-ngx/src/app/core/auth/auth.models.ts
  3. 10
      ui-ngx/src/app/core/auth/auth.reducer.ts
  4. 18
      ui-ngx/src/app/core/auth/auth.selectors.ts
  5. 19
      ui-ngx/src/app/core/auth/auth.service.ts
  6. 2
      ui-ngx/src/app/core/http/public-api.ts
  7. 58
      ui-ngx/src/app/core/http/user-preferences.service.ts
  8. 54
      ui-ngx/src/app/core/http/user-settings.service.ts
  9. 2
      ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
  10. 4
      ui-ngx/src/app/modules/home/models/services.map.ts
  11. 2
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  12. 2
      ui-ngx/src/app/shared/models/public-api.ts
  13. 4
      ui-ngx/src/app/shared/models/user-settings.models.ts

8
ui-ngx/src/app/core/auth/auth.effects.ts

@ -18,7 +18,7 @@ import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UserPreferencesService } from '@core/http/user-preferences.service'; import { UserSettingsService } from '@core/http/user-settings.service';
import { mergeMap, withLatestFrom } from 'rxjs/operators'; import { mergeMap, withLatestFrom } from 'rxjs/operators';
import { AuthActions, AuthActionTypes } from '@core/auth/auth.actions'; import { AuthActions, AuthActionTypes } from '@core/auth/auth.actions';
import { selectAuthState } from '@core/auth/auth.selectors'; import { selectAuthState } from '@core/auth/auth.selectors';
@ -28,15 +28,15 @@ export class AuthEffects {
constructor( constructor(
private actions$: Actions<AuthActions>, private actions$: Actions<AuthActions>,
private store: Store<AppState>, private store: Store<AppState>,
private userPreferencesService: UserPreferencesService private userSettingsService: UserSettingsService
) { ) {
} }
persistUserPreferences = createEffect(() => this.actions$.pipe( persistOpenedMenuSections = createEffect(() => this.actions$.pipe(
ofType( ofType(
AuthActionTypes.UPDATE_OPENED_MENU_SECTION, AuthActionTypes.UPDATE_OPENED_MENU_SECTION,
), ),
withLatestFrom(this.store.pipe(select(selectAuthState))), withLatestFrom(this.store.pipe(select(selectAuthState))),
mergeMap(([action, state]) => this.userPreferencesService.saveUserPreferences(state.authUser, state.userPreferences)) mergeMap(([action, state]) => this.userSettingsService.putUserSettings({ openedMenuSections: state.userSettings.openedMenuSections }))
), {dispatch: false}); ), {dispatch: false});
} }

4
ui-ngx/src/app/core/auth/auth.models.ts

@ -15,7 +15,7 @@
/// ///
import { AuthUser, User } from '@shared/models/user.model'; import { AuthUser, User } from '@shared/models/user.model';
import { UserPreferences } from '@shared/models/user-preferences.models'; import { UserSettings } from '@shared/models/user-settings.models';
export interface SysParamsState { export interface SysParamsState {
userTokenAccessEnabled: boolean; userTokenAccessEnabled: boolean;
@ -23,7 +23,7 @@ export interface SysParamsState {
edgesSupportEnabled: boolean; edgesSupportEnabled: boolean;
hasRepository: boolean; hasRepository: boolean;
tbelEnabled: boolean; tbelEnabled: boolean;
userPreferences: UserPreferences; userSettings: UserSettings;
} }
export interface AuthPayload extends SysParamsState { export interface AuthPayload extends SysParamsState {

10
ui-ngx/src/app/core/auth/auth.reducer.ts

@ -16,7 +16,7 @@
import { AuthPayload, AuthState } from './auth.models'; import { AuthPayload, AuthState } from './auth.models';
import { AuthActions, AuthActionTypes } from './auth.actions'; import { AuthActions, AuthActionTypes } from './auth.actions';
import { initialUserPreferences } from '@shared/models/user-preferences.models'; import { initialUserSettings } from '@shared/models/user-settings.models';
const emptyUserAuthState: AuthPayload = { const emptyUserAuthState: AuthPayload = {
authUser: null, authUser: null,
@ -27,7 +27,7 @@ const emptyUserAuthState: AuthPayload = {
edgesSupportEnabled: false, edgesSupportEnabled: false,
hasRepository: false, hasRepository: false,
tbelEnabled: false, tbelEnabled: false,
userPreferences: initialUserPreferences userSettings: initialUserSettings
}; };
export const initialState: AuthState = { export const initialState: AuthState = {
@ -62,7 +62,7 @@ export const authReducer = (
return { ...state, ...action.payload}; return { ...state, ...action.payload};
case AuthActionTypes.UPDATE_OPENED_MENU_SECTION: case AuthActionTypes.UPDATE_OPENED_MENU_SECTION:
const openedMenuSections = new Set(state.userPreferences.openedMenuSections); const openedMenuSections = new Set(state.userSettings.openedMenuSections);
if (action.payload.opened) { if (action.payload.opened) {
if (!openedMenuSections.has(action.payload.path)) { if (!openedMenuSections.has(action.payload.path)) {
openedMenuSections.add(action.payload.path); openedMenuSections.add(action.payload.path);
@ -70,8 +70,8 @@ export const authReducer = (
} else { } else {
openedMenuSections.delete(action.payload.path); openedMenuSections.delete(action.payload.path);
} }
const userPreferences = {...state.userPreferences, ...{ openedMenuSections: Array.from(openedMenuSections)}}; const userSettings = {...state.userSettings, ...{ openedMenuSections: Array.from(openedMenuSections)}};
return { ...state, ...{ userPreferences }}; return { ...state, ...{ userSettings }};
default: default:
return state; return state;

18
ui-ngx/src/app/core/auth/auth.selectors.ts

@ -20,7 +20,7 @@ import { AppState } from '../core.state';
import { AuthState } from './auth.models'; import { AuthState } from './auth.models';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { AuthUser } from '@shared/models/user.model'; import { AuthUser } from '@shared/models/user.model';
import { UserPreferences } from '@shared/models/user-preferences.models'; import { UserSettings } from '@shared/models/user-settings.models';
export const selectAuthState = createFeatureSelector< AuthState>( export const selectAuthState = createFeatureSelector< AuthState>(
'auth' 'auth'
@ -66,14 +66,14 @@ export const selectTbelEnabled = createSelector(
(state: AuthState) => state.tbelEnabled (state: AuthState) => state.tbelEnabled
); );
export const selectUserPreferences = createSelector( export const selectUserSettings = createSelector(
selectAuthState, selectAuthState,
(state: AuthState) => state.userPreferences (state: AuthState) => state.userSettings
); );
export const selectOpenedMenuSections = createSelector( export const selectOpenedMenuSections = createSelector(
selectAuthState, selectAuthState,
(state: AuthState) => state.userPreferences.openedMenuSections (state: AuthState) => state.userSettings.openedMenuSections
); );
@ -93,12 +93,12 @@ export const getCurrentAuthUser = (store: Store<AppState>): AuthUser => {
return authUser; return authUser;
}; };
export const getCurrentUserPreferences = (store: Store<AppState>): UserPreferences => { export const getCurrentUserSettings = (store: Store<AppState>): UserSettings => {
let userPreferences: UserPreferences; let userSettings: UserSettings;
store.pipe(select(selectUserPreferences), take(1)).subscribe( store.pipe(select(selectUserSettings), take(1)).subscribe(
val => userPreferences = val val => userSettings = val
); );
return userPreferences; return userSettings;
}; };
export const getCurrentOpenedMenuSections = (store: Store<AppState>): string[] => { export const getCurrentOpenedMenuSections = (store: Store<AppState>): string[] => {

19
ui-ngx/src/app/core/auth/auth.service.ts

@ -48,8 +48,8 @@ import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models';
import { isMobileApp } from '@core/utils'; import { isMobileApp } from '@core/utils';
import { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models'; import { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models';
import { UserPasswordPolicy } from '@shared/models/settings.models'; import { UserPasswordPolicy } from '@shared/models/settings.models';
import { UserPreferences } from '@shared/models/user-preferences.models'; import { UserSettings } from '@shared/models/user-settings.models';
import { UserPreferencesService } from '@core/http/user-preferences.service'; import { UserSettingsService } from '@core/http/user-settings.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -68,7 +68,7 @@ export class AuthService {
private dashboardService: DashboardService, private dashboardService: DashboardService,
private adminService: AdminService, private adminService: AdminService,
private translate: TranslateService, private translate: TranslateService,
private userPreferencesService: UserPreferencesService, private userSettingsService: UserSettingsService,
private dialog: MatDialog private dialog: MatDialog
) { ) {
} }
@ -130,7 +130,8 @@ export class AuthService {
} }
public checkTwoFaVerificationCode(providerType: TwoFactorAuthProviderType, verificationCode: number): Observable<LoginResponse> { public checkTwoFaVerificationCode(providerType: TwoFactorAuthProviderType, verificationCode: number): Observable<LoginResponse> {
return this.http.post<LoginResponse>(`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`, return this.http.post<LoginResponse>
(`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`,
null, defaultHttpOptions(false, true)).pipe( null, defaultHttpOptions(false, true)).pipe(
tap((loginResponse: LoginResponse) => { tap((loginResponse: LoginResponse) => {
this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true); this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true);
@ -493,8 +494,8 @@ export class AuthService {
} }
} }
private loadUserPreferences(authUser: AuthUser): Observable<UserPreferences> { private loadUserSettings(): Observable<UserSettings> {
return this.userPreferencesService.loadUserPreferences(authUser); return this.userSettingsService.loadUserSettings();
} }
private loadSystemParams(authPayload: AuthPayload): Observable<SysParamsState> { private loadSystemParams(authPayload: AuthPayload): Observable<SysParamsState> {
@ -503,7 +504,7 @@ export class AuthService {
this.loadIsEdgesSupportEnabled(), this.loadIsEdgesSupportEnabled(),
this.loadHasRepository(authPayload.authUser), this.loadHasRepository(authPayload.authUser),
this.loadTbelEnabled(authPayload.authUser), this.loadTbelEnabled(authPayload.authUser),
this.loadUserPreferences(authPayload.authUser), this.loadUserSettings(),
this.timeService.loadMaxDatapointsLimit()]; this.timeService.loadMaxDatapointsLimit()];
return forkJoin(sources) return forkJoin(sources)
.pipe(map((data) => { .pipe(map((data) => {
@ -512,8 +513,8 @@ export class AuthService {
const edgesSupportEnabled: boolean = data[2] as boolean; const edgesSupportEnabled: boolean = data[2] as boolean;
const hasRepository: boolean = data[3] as boolean; const hasRepository: boolean = data[3] as boolean;
const tbelEnabled: boolean = data[4] as boolean; const tbelEnabled: boolean = data[4] as boolean;
const userPreferences = data[5] as UserPreferences; const userSettings = data[5] as UserSettings;
return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled, userPreferences}; return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled, userSettings};
}, catchError((err) => of({})))); }, catchError((err) => of({}))));
} }

2
ui-ngx/src/app/core/http/public-api.ts

@ -39,5 +39,5 @@ export * from './tenant.service';
export * from './tenant-profile.service'; export * from './tenant-profile.service';
export * from './ui-settings.service'; export * from './ui-settings.service';
export * from './user.service'; export * from './user.service';
export * from './user-preferences.service'; export * from './user-settings.service';
export * from './widget.service'; export * from './widget.service';

58
ui-ngx/src/app/core/http/user-preferences.service.ts

@ -1,58 +0,0 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Injectable } from '@angular/core';
import { AttributeService } from '@core/http/attribute.service';
import { Observable } from 'rxjs';
import { initialUserPreferences, UserPreferences } from '@shared/models/user-preferences.models';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityType } from '@shared/models/entity-type.models';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { map } from 'rxjs/operators';
import { AuthUser } from '@shared/models/user.model';
const USER_PREFERENCES_ATTRIBUTE_KEY = 'user_preferences';
@Injectable({
providedIn: 'root'
})
export class UserPreferencesService {
constructor(
private store: Store<AppState>,
private attributeService: AttributeService
) {}
public loadUserPreferences(authUser: AuthUser): Observable<UserPreferences> {
return this.attributeService.getEntityAttributes({id: authUser.userId, entityType: EntityType.USER},
AttributeScope.SERVER_SCOPE, [USER_PREFERENCES_ATTRIBUTE_KEY], { ignoreLoading: true, ignoreErrors: true }).pipe(
map((attributes) => {
const found = ((attributes || []).find(data => data.key === USER_PREFERENCES_ATTRIBUTE_KEY));
return found ? JSON.parse(found.value) : initialUserPreferences;
})
);
}
public saveUserPreferences(authUser: AuthUser, userPreferences: UserPreferences): Observable<void> {
return this.attributeService.saveEntityAttributes({id: authUser.userId, entityType: EntityType.USER},
AttributeScope.SERVER_SCOPE,
[{key: USER_PREFERENCES_ATTRIBUTE_KEY, value: JSON.stringify(userPreferences)}],
{ ignoreLoading: true, ignoreErrors: true });
}
}

54
ui-ngx/src/app/core/http/user-settings.service.ts

@ -0,0 +1,54 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { initialUserSettings, UserSettings } from '@shared/models/user-settings.models';
import { map } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { defaultHttpOptionsFromConfig } from '@core/http/http-utils';
@Injectable({
providedIn: 'root'
})
export class UserSettingsService {
constructor(
private http: HttpClient
) {}
public loadUserSettings(): Observable<UserSettings> {
return this.http.get<UserSettings>('/api/user/settings', defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})).pipe(
map((settings) => (!settings || !Object.keys(settings).length) ? initialUserSettings : settings)
);
}
public saveUserSettings(userSettings: UserSettings): Observable<UserSettings> {
return this.http.post<UserSettings>('/api/user/settings', userSettings,
defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}));
}
public putUserSettings(userSettingsData: Partial<UserSettings>): Observable<void> {
return this.http.put<void>('/api/user/settings', userSettingsData,
defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}));
}
public deleteUserSettings(paths: string[]) {
return this.http.delete(`/api/user/settings/${paths.join(',')}`,
defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}));
}
}

2
ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts

@ -47,6 +47,7 @@ import { DomSanitizer } from '@angular/platform-browser';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TbInject } from '@shared/decorators/tb-inject'; import { TbInject } from '@shared/decorators/tb-inject';
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe'; import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
import { UserSettingsService } from '@core/http/user-settings.service';
@Directive() @Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix // eslint-disable-next-line @angular-eslint/directive-class-suffix
@ -82,6 +83,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
this.ctx.dialogs = $injector.get(DialogService); this.ctx.dialogs = $injector.get(DialogService);
this.ctx.customDialog = $injector.get(CustomDialogService); this.ctx.customDialog = $injector.get(CustomDialogService);
this.ctx.resourceService = $injector.get(ResourceService); this.ctx.resourceService = $injector.get(ResourceService);
this.ctx.userSettingsService = $injector.get(UserSettingsService);
this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService); this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService);
this.ctx.date = $injector.get(DatePipe); this.ctx.date = $injector.get(DatePipe);
this.ctx.milliSecondsToTimeString = $injector.get(MillisecondsToTimeStringPipe); this.ctx.milliSecondsToTimeString = $injector.get(MillisecondsToTimeStringPipe);

4
ui-ngx/src/app/modules/home/models/services.map.ts

@ -41,6 +41,7 @@ import { ResourceService } from '@core/http/resource.service';
import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service';
import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service';
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe'; import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
import { UserSettingsService } from '@core/http/user-settings.service';
export const ServicesMap = new Map<string, Type<any>>( export const ServicesMap = new Map<string, Type<any>>(
[ [
@ -69,6 +70,7 @@ export const ServicesMap = new Map<string, Type<any>>(
['authService', AuthService], ['authService', AuthService],
['resourceService', ResourceService], ['resourceService', ResourceService],
['twoFactorAuthenticationService', TwoFactorAuthenticationService], ['twoFactorAuthenticationService', TwoFactorAuthenticationService],
['telemetryWsService', TelemetryWebsocketService] ['telemetryWsService', TelemetryWebsocketService],
['userSettingsService', UserSettingsService]
] ]
); );

2
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -89,6 +89,7 @@ import { TbPopoverComponent } from '@shared/components/popover.component';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { AlarmQuery, AlarmSearchStatus, AlarmStatus} from '@app/shared/models/alarm.models'; import { AlarmQuery, AlarmSearchStatus, AlarmStatus} from '@app/shared/models/alarm.models';
import { MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api'; import { MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api';
import { UserSettingsService } from '@core/http/user-settings.service';
export interface IWidgetAction { export interface IWidgetAction {
name: string; name: string;
@ -179,6 +180,7 @@ export class WidgetContext {
dialogs: DialogService; dialogs: DialogService;
customDialog: CustomDialogService; customDialog: CustomDialogService;
resourceService: ResourceService; resourceService: ResourceService;
userSettingsService: UserSettingsService;
telemetryWsService: TelemetryWebsocketService; telemetryWsService: TelemetryWebsocketService;
telemetrySubscribers?: TelemetrySubscriber[]; telemetrySubscribers?: TelemetrySubscriber[];
date: DatePipe; date: DatePipe;

2
ui-ngx/src/app/shared/models/public-api.ts

@ -49,7 +49,7 @@ export * from './rule-node.models';
export * from './settings.models'; export * from './settings.models';
export * from './tenant.model'; export * from './tenant.model';
export * from './user.model'; export * from './user.model';
export * from './user-preferences.models'; export * from './user-settings.models';
export * from './widget.models'; export * from './widget.models';
export * from './widgets-bundle.model'; export * from './widgets-bundle.model';
export * from './window-message.model'; export * from './window-message.model';

4
ui-ngx/src/app/shared/models/user-preferences.models.ts → ui-ngx/src/app/shared/models/user-settings.models.ts

@ -14,10 +14,10 @@
/// limitations under the License. /// limitations under the License.
/// ///
export interface UserPreferences { export interface UserSettings {
openedMenuSections?: string[]; openedMenuSections?: string[];
} }
export const initialUserPreferences: UserPreferences = { export const initialUserSettings: UserSettings = {
openedMenuSections: [] openedMenuSections: []
}; };
Loading…
Cancel
Save