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 { select, Store } from '@ngrx/store';
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 { AuthActions, AuthActionTypes } from '@core/auth/auth.actions';
import { selectAuthState } from '@core/auth/auth.selectors';
@ -28,15 +28,15 @@ export class AuthEffects {
constructor(
private actions$: Actions<AuthActions>,
private store: Store<AppState>,
private userPreferencesService: UserPreferencesService
private userSettingsService: UserSettingsService
) {
}
persistUserPreferences = createEffect(() => this.actions$.pipe(
persistOpenedMenuSections = createEffect(() => this.actions$.pipe(
ofType(
AuthActionTypes.UPDATE_OPENED_MENU_SECTION,
),
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});
}

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

@ -15,7 +15,7 @@
///
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 {
userTokenAccessEnabled: boolean;
@ -23,7 +23,7 @@ export interface SysParamsState {
edgesSupportEnabled: boolean;
hasRepository: boolean;
tbelEnabled: boolean;
userPreferences: UserPreferences;
userSettings: UserSettings;
}
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 { AuthActions, AuthActionTypes } from './auth.actions';
import { initialUserPreferences } from '@shared/models/user-preferences.models';
import { initialUserSettings } from '@shared/models/user-settings.models';
const emptyUserAuthState: AuthPayload = {
authUser: null,
@ -27,7 +27,7 @@ const emptyUserAuthState: AuthPayload = {
edgesSupportEnabled: false,
hasRepository: false,
tbelEnabled: false,
userPreferences: initialUserPreferences
userSettings: initialUserSettings
};
export const initialState: AuthState = {
@ -62,7 +62,7 @@ export const authReducer = (
return { ...state, ...action.payload};
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 (!openedMenuSections.has(action.payload.path)) {
openedMenuSections.add(action.payload.path);
@ -70,8 +70,8 @@ export const authReducer = (
} else {
openedMenuSections.delete(action.payload.path);
}
const userPreferences = {...state.userPreferences, ...{ openedMenuSections: Array.from(openedMenuSections)}};
return { ...state, ...{ userPreferences }};
const userSettings = {...state.userSettings, ...{ openedMenuSections: Array.from(openedMenuSections)}};
return { ...state, ...{ userSettings }};
default:
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 { take } from 'rxjs/operators';
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>(
'auth'
@ -66,14 +66,14 @@ export const selectTbelEnabled = createSelector(
(state: AuthState) => state.tbelEnabled
);
export const selectUserPreferences = createSelector(
export const selectUserSettings = createSelector(
selectAuthState,
(state: AuthState) => state.userPreferences
(state: AuthState) => state.userSettings
);
export const selectOpenedMenuSections = createSelector(
selectAuthState,
(state: AuthState) => state.userPreferences.openedMenuSections
(state: AuthState) => state.userSettings.openedMenuSections
);
@ -93,12 +93,12 @@ export const getCurrentAuthUser = (store: Store<AppState>): AuthUser => {
return authUser;
};
export const getCurrentUserPreferences = (store: Store<AppState>): UserPreferences => {
let userPreferences: UserPreferences;
store.pipe(select(selectUserPreferences), take(1)).subscribe(
val => userPreferences = val
export const getCurrentUserSettings = (store: Store<AppState>): UserSettings => {
let userSettings: UserSettings;
store.pipe(select(selectUserSettings), take(1)).subscribe(
val => userSettings = val
);
return userPreferences;
return userSettings;
};
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 { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models';
import { UserPasswordPolicy } from '@shared/models/settings.models';
import { UserPreferences } from '@shared/models/user-preferences.models';
import { UserPreferencesService } from '@core/http/user-preferences.service';
import { UserSettings } from '@shared/models/user-settings.models';
import { UserSettingsService } from '@core/http/user-settings.service';
@Injectable({
providedIn: 'root'
@ -68,7 +68,7 @@ export class AuthService {
private dashboardService: DashboardService,
private adminService: AdminService,
private translate: TranslateService,
private userPreferencesService: UserPreferencesService,
private userSettingsService: UserSettingsService,
private dialog: MatDialog
) {
}
@ -130,7 +130,8 @@ export class AuthService {
}
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(
tap((loginResponse: LoginResponse) => {
this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true);
@ -493,8 +494,8 @@ export class AuthService {
}
}
private loadUserPreferences(authUser: AuthUser): Observable<UserPreferences> {
return this.userPreferencesService.loadUserPreferences(authUser);
private loadUserSettings(): Observable<UserSettings> {
return this.userSettingsService.loadUserSettings();
}
private loadSystemParams(authPayload: AuthPayload): Observable<SysParamsState> {
@ -503,7 +504,7 @@ export class AuthService {
this.loadIsEdgesSupportEnabled(),
this.loadHasRepository(authPayload.authUser),
this.loadTbelEnabled(authPayload.authUser),
this.loadUserPreferences(authPayload.authUser),
this.loadUserSettings(),
this.timeService.loadMaxDatapointsLimit()];
return forkJoin(sources)
.pipe(map((data) => {
@ -512,8 +513,8 @@ export class AuthService {
const edgesSupportEnabled: boolean = data[2] as boolean;
const hasRepository: boolean = data[3] as boolean;
const tbelEnabled: boolean = data[4] as boolean;
const userPreferences = data[5] as UserPreferences;
return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled, userPreferences};
const userSettings = data[5] as UserSettings;
return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled, userSettings};
}, 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 './ui-settings.service';
export * from './user.service';
export * from './user-preferences.service';
export * from './user-settings.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 { TbInject } from '@shared/decorators/tb-inject';
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
import { UserSettingsService } from '@core/http/user-settings.service';
@Directive()
// 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.customDialog = $injector.get(CustomDialogService);
this.ctx.resourceService = $injector.get(ResourceService);
this.ctx.userSettingsService = $injector.get(UserSettingsService);
this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService);
this.ctx.date = $injector.get(DatePipe);
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 { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service';
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>>(
[
@ -69,6 +70,7 @@ export const ServicesMap = new Map<string, Type<any>>(
['authService', AuthService],
['resourceService', ResourceService],
['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 { AlarmQuery, AlarmSearchStatus, AlarmStatus} from '@app/shared/models/alarm.models';
import { MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api';
import { UserSettingsService } from '@core/http/user-settings.service';
export interface IWidgetAction {
name: string;
@ -179,6 +180,7 @@ export class WidgetContext {
dialogs: DialogService;
customDialog: CustomDialogService;
resourceService: ResourceService;
userSettingsService: UserSettingsService;
telemetryWsService: TelemetryWebsocketService;
telemetrySubscribers?: TelemetrySubscriber[];
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 './tenant.model';
export * from './user.model';
export * from './user-preferences.models';
export * from './user-settings.models';
export * from './widget.models';
export * from './widgets-bundle.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.
///
export interface UserPreferences {
export interface UserSettings {
openedMenuSections?: string[];
}
export const initialUserPreferences: UserPreferences = {
export const initialUserSettings: UserSettings = {
openedMenuSections: []
};
Loading…
Cancel
Save