diff --git a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts index 79fa5a81ec..cc84cfebdd 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts @@ -1,26 +1,23 @@ import { Injectable, Pipe, PipeTransform } from '@angular/core'; -import { Store } from '@ngxs/store'; import { Config } from '../models'; -import { ConfigState } from '../states'; +import { LocalizationService } from '../services/localization.service'; @Injectable() @Pipe({ name: 'abpLocalization', }) export class LocalizationPipe implements PipeTransform { - constructor(private store: Store) {} + constructor(private localization: LocalizationService) {} transform( value: string | Config.LocalizationWithDefault = '', ...interpolateParams: string[] ): string { - return this.store.selectSnapshot( - ConfigState.getLocalization( - value, - ...interpolateParams.reduce( - (acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), - [], - ), + return this.localization.instant( + value, + ...interpolateParams.reduce( + (acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), + [], ), ); } diff --git a/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts b/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts index 39b893501c..5be97fbb64 100644 --- a/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts @@ -1,20 +1,19 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { Rest } from '../models/rest'; import { ApplicationConfiguration } from '../models/application-configuration'; +import { Rest } from '../models/rest'; +import { EnvironmentService } from './environment.service'; import { RestService } from './rest.service'; -import { Store } from '@ngxs/store'; -import { ConfigState } from '../states/config.state'; @Injectable({ providedIn: 'root', }) export class ApplicationConfigurationService { get apiName(): string { - return this.store.selectSnapshot(ConfigState.getDeep('environment.application.name')); + return this.environment.getEnvironment().application?.name; } - constructor(private rest: RestService, private store: Store) {} + constructor(private rest: RestService, private environment: EnvironmentService) {} getConfiguration(): Observable { const request: Rest.Request = { diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts index d694691701..817bd57f94 100644 --- a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts @@ -1,15 +1,15 @@ import { HttpHeaders } from '@angular/common/http'; import { Inject, Injectable, Injector, Optional } from '@angular/core'; import { Navigate } from '@ngxs/router-plugin'; -import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; +import { Store } from '@ngxs/store'; import { OAuthService } from 'angular-oauth2-oidc'; import { from, Observable } from 'rxjs'; import { switchMap, take, tap } from 'rxjs/operators'; import snq from 'snq'; -import { GetAppConfiguration, SetEnvironment } from '../actions/config.actions'; -import { ConfigState } from '../states/config.state'; import { AuthFlowStrategy, AUTH_FLOW_STRATEGY } from '../strategies/auth-flow.strategy'; -import { RestService } from './rest.service'; +import { ApplicationConfigurationService } from './application-configuration.service'; +import { ConfigStateService } from './config-state.service'; +import { EnvironmentService } from './environment.service'; import { SessionStateService } from './session-state.service'; @Injectable({ @@ -24,12 +24,13 @@ export class AuthService { } constructor( - private actions: Actions, + private environment: EnvironmentService, private injector: Injector, - private rest: RestService, private oAuthService: OAuthService, private store: Store, private sessionState: SessionStateService, + private configState: ConfigStateService, + private appConfigService: ApplicationConfigurationService, @Optional() @Inject('ACCOUNT_OPTIONS') private options: any, ) { this.setStrategy(); @@ -37,9 +38,7 @@ export class AuthService { } private setStrategy = () => { - const flow = - this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.responseType')) || - 'password'; + const flow = this.environment.getEnvironment().oAuthConfig.responseType || 'password'; if (this.flow === flow) return; if (this.strategy) this.strategy.destroy(); @@ -52,7 +51,7 @@ export class AuthService { }; private listenToSetEnvironment() { - this.actions.pipe(ofActionSuccessful(SetEnvironment)).subscribe(this.setStrategy); + this.environment.onUpdate$(state => state.oAuthConfig).subscribe(this.setStrategy); } login(username: string, password: string): Observable { @@ -65,7 +64,9 @@ export class AuthService { new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), ), ).pipe( - switchMap(() => this.store.dispatch(new GetAppConfiguration())), + switchMap(() => + this.appConfigService.getConfiguration().pipe(tap(res => this.configState.setState(res))), + ), tap(() => { const redirectUrl = snq(() => window.history.state.redirectUrl) || (this.options || {}).redirectUrl || '/'; diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index 670a2fe5f1..5b294c4905 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -3,6 +3,7 @@ export * from './auth.service'; export * from './config-state.service'; export * from './content-projection.service'; export * from './dom-insertion.service'; +export * from './environment.service'; export * from './lazy-load.service'; export * from './list.service'; export * from './localization.service'; diff --git a/npm/ng-packs/packages/core/src/lib/services/permission.service.ts b/npm/ng-packs/packages/core/src/lib/services/permission.service.ts index 522d8b57ae..eb06fad098 100644 --- a/npm/ng-packs/packages/core/src/lib/services/permission.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/permission.service.ts @@ -1,13 +1,12 @@ -import { ConfigState } from '../states'; -import { Store } from '@ngxs/store'; +import { Injectable } from '@angular/core'; import { map } from 'rxjs/operators'; -import { ApplicationConfiguration } from '../models/application-configuration'; import snq from 'snq'; -import { Injectable } from '@angular/core'; +import { ApplicationConfiguration } from '../models/application-configuration'; +import { ConfigStateService } from './config-state.service'; @Injectable({ providedIn: 'root' }) export class PermissionService { - constructor(private store: Store) {} + constructor(private configState: ConfigStateService) {} getGrantedPolicy$(key: string) { return this.getStream().pipe(map(policies => this.isPolicyGranted(key, policies))); @@ -43,11 +42,11 @@ export class PermissionService { } private getStream() { - return this.store.select(ConfigState).pipe(map(this.mapToPolicies)); + return this.configState.getAll$().pipe(map(this.mapToPolicies)); } private getSnapshot() { - return this.mapToPolicies(this.store.selectSnapshot(ConfigState)); + return this.mapToPolicies(this.configState.getAll()); } private mapToPolicies(applicationConfiguration: ApplicationConfiguration.Response) { diff --git a/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts b/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts index 29c7ddf07e..df0f9231e9 100644 --- a/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts @@ -8,7 +8,7 @@ import { reloadRoute } from '../utils/route-utils'; @Injectable({ providedIn: 'root' }) export class ReplaceableComponentsService { - private store: InternalStore; + private readonly store: InternalStore; get replaceableComponents$(): Observable { return this.store.sliceState(state => state); diff --git a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts index 4ece11a43f..bd2c41cd29 100644 --- a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts @@ -5,10 +5,10 @@ import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { RestOccurError } from '../actions/rest.actions'; import { Rest } from '../models/rest'; -import { ConfigState } from '../states/config.state'; import { isUndefinedOrEmptyString } from '../utils/common-utils'; import { ABP } from '../models/common'; import { CORE_OPTIONS } from '../tokens/options.token'; +import { EnvironmentService } from './environment.service'; @Injectable({ providedIn: 'root', @@ -18,10 +18,11 @@ export class RestService { @Inject(CORE_OPTIONS) private options: ABP.Root, private http: HttpClient, private store: Store, + private environment: EnvironmentService, ) {} private getApiFromStore(apiName: string): string { - return this.store.selectSnapshot(ConfigState.getApiUrl(apiName)); + return this.environment.getApiUrl(apiName); } handleError(err: any): Observable { diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 3ce32401d2..0d589ff440 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -1,10 +1,10 @@ import { Injectable, Injector, OnDestroy } from '@angular/core'; -import { Actions, ofActionSuccessful } from '@ngxs/store'; +import { Actions } from '@ngxs/store'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; -import { GetAppConfiguration } from '../actions/config.actions'; import { ABP } from '../models/common'; import { pushValueTo } from '../utils/array-utils'; import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; +import { ConfigStateService } from './config-state.service'; import { PermissionService } from './permission.service'; export abstract class AbstractTreeService { @@ -152,10 +152,8 @@ export abstract class AbstractNavTreeService constructor(protected injector: Injector) { super(); - this.actions = injector.get(Actions); - this.subscription = this.actions - .pipe(ofActionSuccessful(GetAppConfiguration)) - .subscribe(() => this.refresh()); + const configState = this.injector.get(ConfigStateService); + this.subscription = configState.onUpdate$(state => state).subscribe(() => this.refresh()); this.permissionService = injector.get(PermissionService); } diff --git a/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts index 9d2cc24ed2..1af9a890ad 100644 --- a/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts @@ -3,6 +3,8 @@ import { ApplicationConfiguration } from '../models/application-configuration'; import { Session } from '../models/session'; import { InternalStore } from '../utils/internal-store-utils'; import compare from 'just-compare'; +import { ConfigStateService } from './config-state.service'; +import { filter, take, tap } from 'rxjs/operators'; export interface SessionDetail { openedTabCount: number; @@ -20,8 +22,9 @@ export class SessionStateService { localStorage.setItem('abpSession', JSON.stringify(this.store.state)); }; - constructor() { + constructor(private configState: ConfigStateService) { this.init(); + this.setInitialLanguage(); } private init() { @@ -33,6 +36,25 @@ export class SessionStateService { this.store.sliceUpdate(state => state).subscribe(this.updateLocalStorage); } + private setInitialLanguage() { + if (this.getLanguage()) return; + + this.configState + .getDeep$('localization.currentCulture.cultureName') + .pipe( + tap(console.warn), + filter(cultureName => !!cultureName), + take(1), + ) + .subscribe(lang => { + if (lang.includes(';')) { + lang = lang.split(';')[0]; + } + + this.setLanguage(lang); + }); + } + onLanguageChange$() { return this.store.sliceUpdate(state => state.language); } @@ -67,5 +89,6 @@ export class SessionStateService { if (language === this.store.state.language) return; this.store.patch({ language }); + document.documentElement.setAttribute('lang', language); } } diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts index e301ad19cb..60b2cd9702 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts @@ -4,10 +4,11 @@ import { Store } from '@ngxs/store'; import { AuthConfig, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; import { Observable, of } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; -import { GetAppConfiguration } from '../actions/config.actions'; import { RestOccurError } from '../actions/rest.actions'; +import { ApplicationConfigurationService } from '../services/application-configuration.service'; +import { ConfigStateService } from '../services/config-state.service'; +import { EnvironmentService } from '../services/environment.service'; import { RestService } from '../services/rest.service'; -import { ConfigState } from '../states/config.state'; export const oAuthStorage = localStorage; @@ -15,6 +16,9 @@ export abstract class AuthFlowStrategy { abstract readonly isInternalAuth: boolean; protected store: Store; + protected environment: EnvironmentService; + protected configState: ConfigStateService; + protected appConfigService: ApplicationConfigurationService; protected oAuthService: OAuthService; protected oAuthConfig: AuthConfig; abstract checkIfInternalAuth(): boolean; @@ -26,13 +30,16 @@ export abstract class AuthFlowStrategy { constructor(protected injector: Injector) { this.store = injector.get(Store); + this.environment = injector.get(EnvironmentService); + this.configState = injector.get(ConfigStateService); + this.appConfigService = injector.get(ApplicationConfigurationService); this.oAuthService = injector.get(OAuthService); - this.oAuthConfig = this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig')); + this.oAuthConfig = this.environment.getEnvironment().oAuthConfig; } async init(): Promise { const shouldClear = shouldStorageClear( - this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.clientId')), + this.environment.getEnvironment().oAuthConfig.clientId, oAuthStorage, ); if (shouldClear) clearOAuthStorage(oAuthStorage); @@ -91,7 +98,7 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { logout() { const rest = this.injector.get(RestService); - const issuer = this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.issuer')); + const issuer = this.environment.getEnvironment().oAuthConfig.issuer; return rest .request( { @@ -103,7 +110,9 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { ) .pipe( tap(() => this.oAuthService.logOut()), - switchMap(() => this.store.dispatch(new GetAppConfiguration())), + switchMap(() => + this.appConfigService.getConfiguration().pipe(tap(res => this.configState.setState(res))), + ), ); } diff --git a/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts index 47a8f18b42..6dc9a7de16 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts @@ -1,42 +1,44 @@ import { HttpClient } from '@angular/common/http'; import { Injector } from '@angular/core'; import { Store } from '@ngxs/store'; -import { catchError, switchMap } from 'rxjs/operators'; -import { SetEnvironment } from '../actions/config.actions'; -import { Config } from '../models/config'; +import { catchError, tap } from 'rxjs/operators'; import { RestOccurError } from '../actions/rest.actions'; +import { Environment, RemoteEnv } from '../models/environment'; +import { EnvironmentService } from '../services/environment.service'; import { deepMerge } from './object-utils'; -export function getRemoteEnv(injector: Injector, environment: Partial) { +export function getRemoteEnv(injector: Injector, environment: Partial) { + const environmentService = injector.get(EnvironmentService); + const { remoteEnv } = environment; - const { headers = {}, method = 'GET', url } = remoteEnv || ({} as Config.RemoteEnv); + const { headers = {}, method = 'GET', url } = remoteEnv || ({} as RemoteEnv); if (!url) return Promise.resolve(); const http = injector.get(HttpClient); const store = injector.get(Store); return http - .request(method, url, { headers }) + .request(method, url, { headers }) .pipe( catchError(err => store.dispatch(new RestOccurError(err))), // TODO: Condiser get handle function from a provider - switchMap(env => store.dispatch(mergeEnvironments(environment, env, remoteEnv))), + tap(env => environmentService.setState(mergeEnvironments(environment, env, remoteEnv))), ) .toPromise(); } function mergeEnvironments( - local: Partial, + local: Partial, remote: any, - config: Config.RemoteEnv, -) { + config: RemoteEnv, +): Environment { switch (config.mergeStrategy) { case 'deepmerge': - return new SetEnvironment(deepMerge(local, remote)); + return deepMerge(local, remote); case 'overwrite': case null: case undefined: - return new SetEnvironment(remote); + return remote; default: - return new SetEnvironment(config.mergeStrategy(local, remote)); + return config.mergeStrategy(local, remote); } } diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index f34f46e6f0..c4b8e012ab 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -3,12 +3,14 @@ import { Injector } from '@angular/core'; import { Store } from '@ngxs/store'; import { OAuthService } from 'angular-oauth2-oidc'; import { tap } from 'rxjs/operators'; -import { GetAppConfiguration } from '../actions/config.actions'; import { ApplicationConfiguration } from '../models/application-configuration'; import { ABP } from '../models/common'; +import { Environment } from '../models/environment'; +import { ApplicationConfigurationService } from '../services/application-configuration.service'; import { AuthService } from '../services/auth.service'; +import { ConfigStateService } from '../services/config-state.service'; +import { EnvironmentService } from '../services/environment.service'; import { SessionStateService } from '../services/session-state.service'; -import { ConfigState } from '../states/config.state'; import { clearOAuthStorage } from '../strategies/auth-flow.strategy'; import { CORE_OPTIONS } from '../tokens/options.token'; import { getRemoteEnv } from './environment-utils'; @@ -16,22 +18,26 @@ import { parseTenantFromUrl } from './multi-tenancy-utils'; export function getInitialData(injector: Injector) { const fn = async () => { - const store: Store = injector.get(Store); + const environmentService = injector.get(EnvironmentService); + const configState = injector.get(ConfigStateService); + const appConfigService = injector.get(ApplicationConfigurationService); const options = injector.get(CORE_OPTIONS) as ABP.Root; + environmentService.setState(options.environment as Environment); await getRemoteEnv(injector, options.environment); await parseTenantFromUrl(injector); await injector.get(AuthService).init(); if (options.skipGetAppConfiguration) return; - return store - .dispatch(new GetAppConfiguration()) + return appConfigService + .getConfiguration() .pipe( - tap(() => checkAccessToken(store, injector)), + tap(res => configState.setState(res)), + tap(() => checkAccessToken(injector)), tap(() => { - const currentTenant = store.selectSnapshot( - ConfigState.getDeep('currentTenant'), + const currentTenant = configState.getOne( + 'currentTenant', ) as ApplicationConfiguration.CurrentTenant; if (!currentTenant?.id) return; @@ -44,9 +50,10 @@ export function getInitialData(injector: Injector) { return fn; } -export function checkAccessToken(store: Store, injector: Injector) { +export function checkAccessToken(injector: Injector) { + const configState = injector.get(ConfigStateService); const oAuth = injector.get(OAuthService); - if (oAuth.hasValidAccessToken() && !store.selectSnapshot(ConfigState.getDeep('currentUser.id'))) { + if (oAuth.hasValidAccessToken() && !configState.getDeep('currentUser.id')) { clearOAuthStorage(); } } diff --git a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts index eca1bd3dae..ede1a1cbc8 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts @@ -1,11 +1,10 @@ import { Injector } from '@angular/core'; -import { Store } from '@ngxs/store'; import clone from 'just-clone'; +import { of } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; -import { SetEnvironment } from '../actions'; -import { Config } from '../models/config'; +import { Environment } from '../models/environment'; +import { EnvironmentService } from '../services/environment.service'; import { MultiTenancyService } from '../services/multi-tenancy.service'; -import { ConfigState } from '../states/config.state'; import { createTokenParser } from './string-utils'; const tenancyPlaceholder = '{0}'; @@ -19,17 +18,17 @@ function getCurrentTenancyName(appBaseUrl: string): string { } export async function parseTenantFromUrl(injector: Injector) { - const store: Store = injector.get(Store); + const environmentService = injector.get(EnvironmentService); const multiTenancyService = injector.get(MultiTenancyService); - const environment = store.selectSnapshot(ConfigState.getOne('environment')) as Config.Environment; - const { baseUrl = '' } = environment.application; + const baseUrl = environmentService.getEnvironment()?.application?.baseUrl || ''; const tenancyName = getCurrentTenancyName(baseUrl); if (tenancyName) { multiTenancyService.isTenantBoxVisible = false; + setEnvironment(injector, tenancyName); - return setEnvironment(store, tenancyName) + return of(null) .pipe( switchMap(() => multiTenancyService.findTenantByName(tenancyName, { __tenant: '' })), tap(res => { @@ -44,10 +43,10 @@ export async function parseTenantFromUrl(injector: Injector) { return Promise.resolve(); } -function setEnvironment(store: Store, tenancyName: string) { - const environment = clone( - store.selectSnapshot(ConfigState.getOne('environment')), - ) as Config.Environment; +function setEnvironment(injector: Injector, tenancyName: string) { + const environmentService = injector.get(EnvironmentService); + + const environment = clone(environmentService.getEnvironment()) as Environment; if (environment.application.baseUrl) { environment.application.baseUrl = environment.application.baseUrl.replace( @@ -70,5 +69,5 @@ function setEnvironment(store: Store, tenancyName: string) { }); }); - return store.dispatch(new SetEnvironment(environment)); + return environmentService.setState(environment); } diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index 71e5ae834e..1cde2f2081 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -1,8 +1,8 @@ -import { TrackByService, GetAppConfiguration } from '@abp/ng.core'; +import { ApplicationConfigurationService, ConfigStateService, TrackByService } from '@abp/ng.core'; import { LocaleDirection } from '@abp/ng.theme.shared'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Store } from '@ngxs/store'; -import { finalize } from 'rxjs/operators'; +import { finalize, tap } from 'rxjs/operators'; import { FeatureManagement } from '../../models/feature-management'; import { FeaturesService } from '../../proxy/feature-management/features.service'; import { @@ -65,6 +65,8 @@ export class FeatureManagementComponent public readonly track: TrackByService, protected service: FeaturesService, protected store: Store, + protected configState: ConfigStateService, + protected appConfigService: ApplicationConfigurationService, ) {} openModal() { @@ -115,7 +117,10 @@ export class FeatureManagementComponent if (!this.providerKey) { // to refresh host's features - this.store.dispatch(new GetAppConfiguration()); + this.appConfigService + .getConfiguration() + .pipe(tap(res => this.configState.setState(res))) + .subscribe(); } }); } diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts index 16fb9fe9e2..8ee4fa113c 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts @@ -1,4 +1,8 @@ -import { ApplicationConfiguration, ConfigState, GetAppConfiguration } from '@abp/ng.core'; +import { + ApplicationConfiguration, + ApplicationConfigurationService, + ConfigStateService, +} from '@abp/ng.core'; import { LocaleDirection } from '@abp/ng.theme.shared'; import { Component, EventEmitter, Input, Output, Renderer2, TrackByFunction } from '@angular/core'; import { Select, Store } from '@ngxs/store'; @@ -105,7 +109,11 @@ export class PermissionManagementComponent ); } - constructor(private store: Store, private renderer: Renderer2) {} + constructor( + protected store: Store, + protected configState: ConfigStateService, + protected appConfigService: ApplicationConfigurationService, + ) {} getChecked(name: string) { return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted; @@ -239,7 +247,11 @@ export class PermissionManagementComponent ) .pipe( switchMap(() => - this.shouldFetchAppConfig() ? this.store.dispatch(GetAppConfiguration) : of(null), + this.shouldFetchAppConfig() + ? this.appConfigService + .getConfiguration() + .pipe(tap(res => this.configState.setState(res))) + : of(null), ), finalize(() => (this.modalBusy = false)), ) @@ -282,8 +294,8 @@ export class PermissionManagementComponent } shouldFetchAppConfig() { - const currentUser = this.store.selectSnapshot( - ConfigState.getOne('currentUser'), + const currentUser = this.configState.getOne( + 'currentUser', ) as ApplicationConfiguration.CurrentUser; if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts index 6f362b0ee8..804cf5dc32 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts @@ -1,7 +1,6 @@ -import { ApplicationConfiguration, ConfigState, AuthService } from '@abp/ng.core'; +import { ApplicationConfiguration, AuthService, ConfigStateService } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { Select } from '@ngxs/store'; import { Observable } from 'rxjs'; @Component({ @@ -48,14 +47,19 @@ import { Observable } from 'rxjs'; `, }) export class CurrentUserComponent implements OnInit { - @Select(ConfigState.getOne('currentUser')) - currentUser$: Observable; + currentUser$: Observable = this.configState.getOne$( + 'currentUser', + ); get smallScreen(): boolean { return window.innerWidth < 992; } - constructor(private authService: AuthService, private router: Router) {} + constructor( + private authService: AuthService, + private router: Router, + private configState: ConfigStateService, + ) {} ngOnInit() {} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts index fae3fa0cf4..6df8beef7a 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts @@ -1,6 +1,5 @@ -import { ApplicationConfiguration, ConfigState, SessionStateService } from '@abp/ng.core'; -import { Component, OnInit } from '@angular/core'; -import { Select } from '@ngxs/store'; +import { ApplicationConfiguration, ConfigStateService, SessionStateService } from '@abp/ng.core'; +import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import snq from 'snq'; @@ -44,13 +43,14 @@ import snq from 'snq'; `, }) -export class LanguagesComponent implements OnInit { +export class LanguagesComponent { get smallScreen(): boolean { return window.innerWidth < 992; } - @Select(ConfigState.getDeep('localization.languages')) - languages$: Observable; + languages$: Observable = this.configState.getDeep$( + 'localization.languages', + ); get defaultLanguage$(): Observable { return this.languages$.pipe( @@ -78,9 +78,7 @@ export class LanguagesComponent implements OnInit { return this.sessionState.getLanguage(); } - constructor(private sessionState: SessionStateService) {} - - ngOnInit() {} + constructor(private sessionState: SessionStateService, private configState: ConfigStateService) {} onChangeLang(cultureName: string) { this.sessionState.setLanguage(cultureName); diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts index 9ae9784e26..47f80c3fa1 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts @@ -1,5 +1,4 @@ -import { ABP, ApplicationConfiguration } from '@abp/ng.core'; -import { createSelector, Store } from '@ngxs/store'; +import { ABP, ApplicationConfiguration, ConfigStateService } from '@abp/ng.core'; import { Observable, pipe, zip } from 'rxjs'; import { filter, map, switchMap, take } from 'rxjs/operators'; import { ePropType } from '../enums/props.enum'; @@ -12,51 +11,59 @@ import { createDisplayNameLocalizationPipeKeyGenerator } from './localization.ut import { createExtraPropertyValueResolver } from './props.util'; import { getValidatorsFromProperty } from './validation.util'; -const selectConfig = (state: any) => state.ConfigState; - -const selectObjectExtensions = createSelector( - [selectConfig], - (config: ObjectExtensions.Config) => config.objectExtensions, -); - -const selectLocalization = createSelector( - [selectConfig], - (config: ApplicationConfiguration.Response) => config.localization, -); - -const selectEnums = createSelector( - [selectObjectExtensions, selectLocalization], - (extensions: ObjectExtensions.Item) => - Object.keys(extensions.enums).reduce((acc, key) => { - const { fields, localizationResource } = extensions.enums[key]; - acc[key] = { - fields, - localizationResource, - transformed: createEnum(fields), - }; - return acc; - }, {} as ObjectExtensions.Enums), -); - -const createObjectExtensionEntitiesSelector = (moduleKey: ModuleKey) => - createSelector([selectObjectExtensions], (extensions: ObjectExtensions.Item) => { - if (!extensions) return null; - - return (extensions.modules[moduleKey] || ({} as ObjectExtensions.Module)).entities; - }); - -export function getObjectExtensionEntitiesFromStore(store: Store, moduleKey: ModuleKey) { - return store.select(createObjectExtensionEntitiesSelector(moduleKey)).pipe( +function selectObjectExtensions( + configState: ConfigStateService, +): Observable { + return configState.getOne$('objectExtensions'); +} + +function selectLocalization( + configState: ConfigStateService, +): Observable { + return configState.getOne$('localization'); +} + +function selectEnums( + configState: ConfigStateService, +): Observable> { + return selectObjectExtensions(configState).pipe( + map((extensions: ObjectExtensions.Item) => + Object.keys(extensions.enums).reduce((acc, key) => { + const { fields, localizationResource } = extensions.enums[key]; + acc[key] = { + fields, + localizationResource, + transformed: createEnum(fields), + }; + return acc; + }, {} as ObjectExtensions.Enums), + ), + ); +} + +export function getObjectExtensionEntitiesFromStore( + configState: ConfigStateService, + moduleKey: ModuleKey, +) { + return selectObjectExtensions(configState).pipe( + map(extensions => { + if (!extensions) return null; + + return (extensions.modules[moduleKey] || ({} as ObjectExtensions.Module)).entities; + }), map(entities => (isUndefined(entities) ? {} : entities)), filter(Boolean), take(1), ); } -export function mapEntitiesToContributors(store: Store, resource: string) { +export function mapEntitiesToContributors( + configState: ConfigStateService, + resource: string, +) { return pipe( switchMap(entities => - zip(store.select(selectLocalization), store.select(selectEnums)).pipe( + zip(selectLocalization(configState), selectEnums(configState)).pipe( map(([localization, enums]) => { const generateDisplayName = createDisplayNameLocalizationPipeKeyGenerator(localization); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/utils/date-parser-formatter.ts b/npm/ng-packs/packages/theme-shared/src/lib/utils/date-parser-formatter.ts index 9525f4be3a..cf24e4d82c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/utils/date-parser-formatter.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/utils/date-parser-formatter.ts @@ -1,8 +1,7 @@ -import { ApplicationConfiguration, ConfigState } from '@abp/ng.core'; +import { ApplicationConfiguration, ConfigStateService } from '@abp/ng.core'; import { DatePipe } from '@angular/common'; import { Injectable, Optional } from '@angular/core'; import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; -import { Store } from '@ngxs/store'; function padNumber(value: number) { if (isNumber(value)) { @@ -22,7 +21,7 @@ function toInteger(value: any): number { @Injectable() export class DateParserFormatter extends NgbDateParserFormatter { - constructor(@Optional() private datePipe: DatePipe, private store: Store) { + constructor(@Optional() private datePipe: DatePipe, private configState: ConfigStateService) { super(); } @@ -50,8 +49,8 @@ export class DateParserFormatter extends NgbDateParserFormatter { } format(date: NgbDateStruct): string { - const { shortDatePattern } = (this.store.selectSnapshot( - ConfigState.getOne('localization'), + const { shortDatePattern } = (this.configState.getOne( + 'localization', ) as ApplicationConfiguration.Localization).currentCulture.dateTimeFormat; if (date && this.datePipe) {