Browse Source

feat: implement session-state.service

pull/6156/head
mehmet-erim 6 years ago
parent
commit
8c7b1fa8fb
  1. 17
      npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts
  2. 9
      npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts
  3. 23
      npm/ng-packs/packages/core/src/lib/services/auth.service.ts
  4. 1
      npm/ng-packs/packages/core/src/lib/services/index.ts
  5. 13
      npm/ng-packs/packages/core/src/lib/services/permission.service.ts
  6. 2
      npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts
  7. 5
      npm/ng-packs/packages/core/src/lib/services/rest.service.ts
  8. 10
      npm/ng-packs/packages/core/src/lib/services/routes.service.ts
  9. 25
      npm/ng-packs/packages/core/src/lib/services/session-state.service.ts
  10. 21
      npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts
  11. 28
      npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts
  12. 27
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  13. 25
      npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts
  14. 11
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts
  15. 22
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  16. 14
      npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts
  17. 16
      npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts
  18. 85
      npm/ng-packs/packages/theme-shared/extensions/src/lib/utils/state.util.ts
  19. 9
      npm/ng-packs/packages/theme-shared/src/lib/utils/date-parser-formatter.ts

17
npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts

@ -1,26 +1,23 @@
import { Injectable, Pipe, PipeTransform } from '@angular/core'; import { Injectable, Pipe, PipeTransform } from '@angular/core';
import { Store } from '@ngxs/store';
import { Config } from '../models'; import { Config } from '../models';
import { ConfigState } from '../states'; import { LocalizationService } from '../services/localization.service';
@Injectable() @Injectable()
@Pipe({ @Pipe({
name: 'abpLocalization', name: 'abpLocalization',
}) })
export class LocalizationPipe implements PipeTransform { export class LocalizationPipe implements PipeTransform {
constructor(private store: Store) {} constructor(private localization: LocalizationService) {}
transform( transform(
value: string | Config.LocalizationWithDefault = '', value: string | Config.LocalizationWithDefault = '',
...interpolateParams: string[] ...interpolateParams: string[]
): string { ): string {
return this.store.selectSnapshot( return this.localization.instant(
ConfigState.getLocalization( value,
value, ...interpolateParams.reduce(
...interpolateParams.reduce( (acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]),
(acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), [],
[],
),
), ),
); );
} }

9
npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts

@ -1,20 +1,19 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Rest } from '../models/rest';
import { ApplicationConfiguration } from '../models/application-configuration'; import { ApplicationConfiguration } from '../models/application-configuration';
import { Rest } from '../models/rest';
import { EnvironmentService } from './environment.service';
import { RestService } from './rest.service'; import { RestService } from './rest.service';
import { Store } from '@ngxs/store';
import { ConfigState } from '../states/config.state';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class ApplicationConfigurationService { export class ApplicationConfigurationService {
get apiName(): string { 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<ApplicationConfiguration.Response> { getConfiguration(): Observable<ApplicationConfiguration.Response> {
const request: Rest.Request<null> = { const request: Rest.Request<null> = {

23
npm/ng-packs/packages/core/src/lib/services/auth.service.ts

@ -1,15 +1,15 @@
import { HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Inject, Injectable, Injector, Optional } from '@angular/core'; import { Inject, Injectable, Injector, Optional } from '@angular/core';
import { Navigate } from '@ngxs/router-plugin'; 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 { OAuthService } from 'angular-oauth2-oidc';
import { from, Observable } from 'rxjs'; import { from, Observable } from 'rxjs';
import { switchMap, take, tap } from 'rxjs/operators'; import { switchMap, take, tap } from 'rxjs/operators';
import snq from 'snq'; 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 { 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'; import { SessionStateService } from './session-state.service';
@Injectable({ @Injectable({
@ -24,12 +24,13 @@ export class AuthService {
} }
constructor( constructor(
private actions: Actions, private environment: EnvironmentService,
private injector: Injector, private injector: Injector,
private rest: RestService,
private oAuthService: OAuthService, private oAuthService: OAuthService,
private store: Store, private store: Store,
private sessionState: SessionStateService, private sessionState: SessionStateService,
private configState: ConfigStateService,
private appConfigService: ApplicationConfigurationService,
@Optional() @Inject('ACCOUNT_OPTIONS') private options: any, @Optional() @Inject('ACCOUNT_OPTIONS') private options: any,
) { ) {
this.setStrategy(); this.setStrategy();
@ -37,9 +38,7 @@ export class AuthService {
} }
private setStrategy = () => { private setStrategy = () => {
const flow = const flow = this.environment.getEnvironment().oAuthConfig.responseType || 'password';
this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.responseType')) ||
'password';
if (this.flow === flow) return; if (this.flow === flow) return;
if (this.strategy) this.strategy.destroy(); if (this.strategy) this.strategy.destroy();
@ -52,7 +51,7 @@ export class AuthService {
}; };
private listenToSetEnvironment() { 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<any> { login(username: string, password: string): Observable<any> {
@ -65,7 +64,9 @@ export class AuthService {
new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }),
), ),
).pipe( ).pipe(
switchMap(() => this.store.dispatch(new GetAppConfiguration())), switchMap(() =>
this.appConfigService.getConfiguration().pipe(tap(res => this.configState.setState(res))),
),
tap(() => { tap(() => {
const redirectUrl = const redirectUrl =
snq(() => window.history.state.redirectUrl) || (this.options || {}).redirectUrl || '/'; snq(() => window.history.state.redirectUrl) || (this.options || {}).redirectUrl || '/';

1
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 './config-state.service';
export * from './content-projection.service'; export * from './content-projection.service';
export * from './dom-insertion.service'; export * from './dom-insertion.service';
export * from './environment.service';
export * from './lazy-load.service'; export * from './lazy-load.service';
export * from './list.service'; export * from './list.service';
export * from './localization.service'; export * from './localization.service';

13
npm/ng-packs/packages/core/src/lib/services/permission.service.ts

@ -1,13 +1,12 @@
import { ConfigState } from '../states'; import { Injectable } from '@angular/core';
import { Store } from '@ngxs/store';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { ApplicationConfiguration } from '../models/application-configuration';
import snq from 'snq'; import snq from 'snq';
import { Injectable } from '@angular/core'; import { ApplicationConfiguration } from '../models/application-configuration';
import { ConfigStateService } from './config-state.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class PermissionService { export class PermissionService {
constructor(private store: Store) {} constructor(private configState: ConfigStateService) {}
getGrantedPolicy$(key: string) { getGrantedPolicy$(key: string) {
return this.getStream().pipe(map(policies => this.isPolicyGranted(key, policies))); return this.getStream().pipe(map(policies => this.isPolicyGranted(key, policies)));
@ -43,11 +42,11 @@ export class PermissionService {
} }
private getStream() { private getStream() {
return this.store.select(ConfigState).pipe(map(this.mapToPolicies)); return this.configState.getAll$().pipe(map(this.mapToPolicies));
} }
private getSnapshot() { private getSnapshot() {
return this.mapToPolicies(this.store.selectSnapshot(ConfigState)); return this.mapToPolicies(this.configState.getAll());
} }
private mapToPolicies(applicationConfiguration: ApplicationConfiguration.Response) { private mapToPolicies(applicationConfiguration: ApplicationConfiguration.Response) {

2
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' }) @Injectable({ providedIn: 'root' })
export class ReplaceableComponentsService { export class ReplaceableComponentsService {
private store: InternalStore<ReplaceableComponents.ReplaceableComponent[]>; private readonly store: InternalStore<ReplaceableComponents.ReplaceableComponent[]>;
get replaceableComponents$(): Observable<ReplaceableComponents.ReplaceableComponent[]> { get replaceableComponents$(): Observable<ReplaceableComponents.ReplaceableComponent[]> {
return this.store.sliceState(state => state); return this.store.sliceState(state => state);

5
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 { catchError } from 'rxjs/operators';
import { RestOccurError } from '../actions/rest.actions'; import { RestOccurError } from '../actions/rest.actions';
import { Rest } from '../models/rest'; import { Rest } from '../models/rest';
import { ConfigState } from '../states/config.state';
import { isUndefinedOrEmptyString } from '../utils/common-utils'; import { isUndefinedOrEmptyString } from '../utils/common-utils';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
import { CORE_OPTIONS } from '../tokens/options.token'; import { CORE_OPTIONS } from '../tokens/options.token';
import { EnvironmentService } from './environment.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -18,10 +18,11 @@ export class RestService {
@Inject(CORE_OPTIONS) private options: ABP.Root, @Inject(CORE_OPTIONS) private options: ABP.Root,
private http: HttpClient, private http: HttpClient,
private store: Store, private store: Store,
private environment: EnvironmentService,
) {} ) {}
private getApiFromStore(apiName: string): string { private getApiFromStore(apiName: string): string {
return this.store.selectSnapshot(ConfigState.getApiUrl(apiName)); return this.environment.getApiUrl(apiName);
} }
handleError(err: any): Observable<any> { handleError(err: any): Observable<any> {

10
npm/ng-packs/packages/core/src/lib/services/routes.service.ts

@ -1,10 +1,10 @@
import { Injectable, Injector, OnDestroy } from '@angular/core'; 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 { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { GetAppConfiguration } from '../actions/config.actions';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
import { pushValueTo } from '../utils/array-utils'; import { pushValueTo } from '../utils/array-utils';
import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils';
import { ConfigStateService } from './config-state.service';
import { PermissionService } from './permission.service'; import { PermissionService } from './permission.service';
export abstract class AbstractTreeService<T extends object> { export abstract class AbstractTreeService<T extends object> {
@ -152,10 +152,8 @@ export abstract class AbstractNavTreeService<T extends ABP.Nav>
constructor(protected injector: Injector) { constructor(protected injector: Injector) {
super(); super();
this.actions = injector.get(Actions); const configState = this.injector.get(ConfigStateService);
this.subscription = this.actions this.subscription = configState.onUpdate$(state => state).subscribe(() => this.refresh());
.pipe(ofActionSuccessful(GetAppConfiguration))
.subscribe(() => this.refresh());
this.permissionService = injector.get(PermissionService); this.permissionService = injector.get(PermissionService);
} }

25
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 { Session } from '../models/session';
import { InternalStore } from '../utils/internal-store-utils'; import { InternalStore } from '../utils/internal-store-utils';
import compare from 'just-compare'; import compare from 'just-compare';
import { ConfigStateService } from './config-state.service';
import { filter, take, tap } from 'rxjs/operators';
export interface SessionDetail { export interface SessionDetail {
openedTabCount: number; openedTabCount: number;
@ -20,8 +22,9 @@ export class SessionStateService {
localStorage.setItem('abpSession', JSON.stringify(this.store.state)); localStorage.setItem('abpSession', JSON.stringify(this.store.state));
}; };
constructor() { constructor(private configState: ConfigStateService) {
this.init(); this.init();
this.setInitialLanguage();
} }
private init() { private init() {
@ -33,6 +36,25 @@ export class SessionStateService {
this.store.sliceUpdate(state => state).subscribe(this.updateLocalStorage); 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$() { onLanguageChange$() {
return this.store.sliceUpdate(state => state.language); return this.store.sliceUpdate(state => state.language);
} }
@ -67,5 +89,6 @@ export class SessionStateService {
if (language === this.store.state.language) return; if (language === this.store.state.language) return;
this.store.patch({ language }); this.store.patch({ language });
document.documentElement.setAttribute('lang', language);
} }
} }

21
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 { AuthConfig, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators'; import { switchMap, tap } from 'rxjs/operators';
import { GetAppConfiguration } from '../actions/config.actions';
import { RestOccurError } from '../actions/rest.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 { RestService } from '../services/rest.service';
import { ConfigState } from '../states/config.state';
export const oAuthStorage = localStorage; export const oAuthStorage = localStorage;
@ -15,6 +16,9 @@ export abstract class AuthFlowStrategy {
abstract readonly isInternalAuth: boolean; abstract readonly isInternalAuth: boolean;
protected store: Store; protected store: Store;
protected environment: EnvironmentService;
protected configState: ConfigStateService;
protected appConfigService: ApplicationConfigurationService;
protected oAuthService: OAuthService; protected oAuthService: OAuthService;
protected oAuthConfig: AuthConfig; protected oAuthConfig: AuthConfig;
abstract checkIfInternalAuth(): boolean; abstract checkIfInternalAuth(): boolean;
@ -26,13 +30,16 @@ export abstract class AuthFlowStrategy {
constructor(protected injector: Injector) { constructor(protected injector: Injector) {
this.store = injector.get(Store); 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.oAuthService = injector.get(OAuthService);
this.oAuthConfig = this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig')); this.oAuthConfig = this.environment.getEnvironment().oAuthConfig;
} }
async init(): Promise<any> { async init(): Promise<any> {
const shouldClear = shouldStorageClear( const shouldClear = shouldStorageClear(
this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.clientId')), this.environment.getEnvironment().oAuthConfig.clientId,
oAuthStorage, oAuthStorage,
); );
if (shouldClear) clearOAuthStorage(oAuthStorage); if (shouldClear) clearOAuthStorage(oAuthStorage);
@ -91,7 +98,7 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
logout() { logout() {
const rest = this.injector.get(RestService); 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 return rest
.request( .request(
{ {
@ -103,7 +110,9 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
) )
.pipe( .pipe(
tap(() => this.oAuthService.logOut()), tap(() => this.oAuthService.logOut()),
switchMap(() => this.store.dispatch(new GetAppConfiguration())), switchMap(() =>
this.appConfigService.getConfiguration().pipe(tap(res => this.configState.setState(res))),
),
); );
} }

28
npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts

@ -1,42 +1,44 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import { Store } from '@ngxs/store'; import { Store } from '@ngxs/store';
import { catchError, switchMap } from 'rxjs/operators'; import { catchError, tap } from 'rxjs/operators';
import { SetEnvironment } from '../actions/config.actions';
import { Config } from '../models/config';
import { RestOccurError } from '../actions/rest.actions'; import { RestOccurError } from '../actions/rest.actions';
import { Environment, RemoteEnv } from '../models/environment';
import { EnvironmentService } from '../services/environment.service';
import { deepMerge } from './object-utils'; import { deepMerge } from './object-utils';
export function getRemoteEnv(injector: Injector, environment: Partial<Config.Environment>) { export function getRemoteEnv(injector: Injector, environment: Partial<Environment>) {
const environmentService = injector.get(EnvironmentService);
const { remoteEnv } = environment; 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(); if (!url) return Promise.resolve();
const http = injector.get(HttpClient); const http = injector.get(HttpClient);
const store = injector.get(Store); const store = injector.get(Store);
return http return http
.request<Config.Environment>(method, url, { headers }) .request<Environment>(method, url, { headers })
.pipe( .pipe(
catchError(err => store.dispatch(new RestOccurError(err))), // TODO: Condiser get handle function from a provider 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(); .toPromise();
} }
function mergeEnvironments( function mergeEnvironments(
local: Partial<Config.Environment>, local: Partial<Environment>,
remote: any, remote: any,
config: Config.RemoteEnv, config: RemoteEnv,
) { ): Environment {
switch (config.mergeStrategy) { switch (config.mergeStrategy) {
case 'deepmerge': case 'deepmerge':
return new SetEnvironment(deepMerge(local, remote)); return deepMerge(local, remote);
case 'overwrite': case 'overwrite':
case null: case null:
case undefined: case undefined:
return new SetEnvironment(remote); return remote;
default: default:
return new SetEnvironment(config.mergeStrategy(local, remote)); return config.mergeStrategy(local, remote);
} }
} }

27
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 { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc'; import { OAuthService } from 'angular-oauth2-oidc';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { GetAppConfiguration } from '../actions/config.actions';
import { ApplicationConfiguration } from '../models/application-configuration'; import { ApplicationConfiguration } from '../models/application-configuration';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
import { Environment } from '../models/environment';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { AuthService } from '../services/auth.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 { SessionStateService } from '../services/session-state.service';
import { ConfigState } from '../states/config.state';
import { clearOAuthStorage } from '../strategies/auth-flow.strategy'; import { clearOAuthStorage } from '../strategies/auth-flow.strategy';
import { CORE_OPTIONS } from '../tokens/options.token'; import { CORE_OPTIONS } from '../tokens/options.token';
import { getRemoteEnv } from './environment-utils'; import { getRemoteEnv } from './environment-utils';
@ -16,22 +18,26 @@ import { parseTenantFromUrl } from './multi-tenancy-utils';
export function getInitialData(injector: Injector) { export function getInitialData(injector: Injector) {
const fn = async () => { 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; const options = injector.get(CORE_OPTIONS) as ABP.Root;
environmentService.setState(options.environment as Environment);
await getRemoteEnv(injector, options.environment); await getRemoteEnv(injector, options.environment);
await parseTenantFromUrl(injector); await parseTenantFromUrl(injector);
await injector.get(AuthService).init(); await injector.get(AuthService).init();
if (options.skipGetAppConfiguration) return; if (options.skipGetAppConfiguration) return;
return store return appConfigService
.dispatch(new GetAppConfiguration()) .getConfiguration()
.pipe( .pipe(
tap(() => checkAccessToken(store, injector)), tap(res => configState.setState(res)),
tap(() => checkAccessToken(injector)),
tap(() => { tap(() => {
const currentTenant = store.selectSnapshot( const currentTenant = configState.getOne(
ConfigState.getDeep('currentTenant'), 'currentTenant',
) as ApplicationConfiguration.CurrentTenant; ) as ApplicationConfiguration.CurrentTenant;
if (!currentTenant?.id) return; if (!currentTenant?.id) return;
@ -44,9 +50,10 @@ export function getInitialData(injector: Injector) {
return fn; return fn;
} }
export function checkAccessToken(store: Store, injector: Injector) { export function checkAccessToken(injector: Injector) {
const configState = injector.get(ConfigStateService);
const oAuth = injector.get(OAuthService); const oAuth = injector.get(OAuthService);
if (oAuth.hasValidAccessToken() && !store.selectSnapshot(ConfigState.getDeep('currentUser.id'))) { if (oAuth.hasValidAccessToken() && !configState.getDeep('currentUser.id')) {
clearOAuthStorage(); clearOAuthStorage();
} }
} }

25
npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts

@ -1,11 +1,10 @@
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import { Store } from '@ngxs/store';
import clone from 'just-clone'; import clone from 'just-clone';
import { of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators'; import { switchMap, tap } from 'rxjs/operators';
import { SetEnvironment } from '../actions'; import { Environment } from '../models/environment';
import { Config } from '../models/config'; import { EnvironmentService } from '../services/environment.service';
import { MultiTenancyService } from '../services/multi-tenancy.service'; import { MultiTenancyService } from '../services/multi-tenancy.service';
import { ConfigState } from '../states/config.state';
import { createTokenParser } from './string-utils'; import { createTokenParser } from './string-utils';
const tenancyPlaceholder = '{0}'; const tenancyPlaceholder = '{0}';
@ -19,17 +18,17 @@ function getCurrentTenancyName(appBaseUrl: string): string {
} }
export async function parseTenantFromUrl(injector: Injector) { export async function parseTenantFromUrl(injector: Injector) {
const store: Store = injector.get(Store); const environmentService = injector.get(EnvironmentService);
const multiTenancyService = injector.get(MultiTenancyService); 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); const tenancyName = getCurrentTenancyName(baseUrl);
if (tenancyName) { if (tenancyName) {
multiTenancyService.isTenantBoxVisible = false; multiTenancyService.isTenantBoxVisible = false;
setEnvironment(injector, tenancyName);
return setEnvironment(store, tenancyName) return of(null)
.pipe( .pipe(
switchMap(() => multiTenancyService.findTenantByName(tenancyName, { __tenant: '' })), switchMap(() => multiTenancyService.findTenantByName(tenancyName, { __tenant: '' })),
tap(res => { tap(res => {
@ -44,10 +43,10 @@ export async function parseTenantFromUrl(injector: Injector) {
return Promise.resolve(); return Promise.resolve();
} }
function setEnvironment(store: Store, tenancyName: string) { function setEnvironment(injector: Injector, tenancyName: string) {
const environment = clone( const environmentService = injector.get(EnvironmentService);
store.selectSnapshot(ConfigState.getOne('environment')),
) as Config.Environment; const environment = clone(environmentService.getEnvironment()) as Environment;
if (environment.application.baseUrl) { if (environment.application.baseUrl) {
environment.application.baseUrl = environment.application.baseUrl.replace( 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);
} }

11
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 { LocaleDirection } from '@abp/ng.theme.shared';
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Store } from '@ngxs/store'; import { Store } from '@ngxs/store';
import { finalize } from 'rxjs/operators'; import { finalize, tap } from 'rxjs/operators';
import { FeatureManagement } from '../../models/feature-management'; import { FeatureManagement } from '../../models/feature-management';
import { FeaturesService } from '../../proxy/feature-management/features.service'; import { FeaturesService } from '../../proxy/feature-management/features.service';
import { import {
@ -65,6 +65,8 @@ export class FeatureManagementComponent
public readonly track: TrackByService, public readonly track: TrackByService,
protected service: FeaturesService, protected service: FeaturesService,
protected store: Store, protected store: Store,
protected configState: ConfigStateService,
protected appConfigService: ApplicationConfigurationService,
) {} ) {}
openModal() { openModal() {
@ -115,7 +117,10 @@ export class FeatureManagementComponent
if (!this.providerKey) { if (!this.providerKey) {
// to refresh host's features // to refresh host's features
this.store.dispatch(new GetAppConfiguration()); this.appConfigService
.getConfiguration()
.pipe(tap(res => this.configState.setState(res)))
.subscribe();
} }
}); });
} }

22
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 { LocaleDirection } from '@abp/ng.theme.shared';
import { Component, EventEmitter, Input, Output, Renderer2, TrackByFunction } from '@angular/core'; import { Component, EventEmitter, Input, Output, Renderer2, TrackByFunction } from '@angular/core';
import { Select, Store } from '@ngxs/store'; 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) { getChecked(name: string) {
return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted; return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted;
@ -239,7 +247,11 @@ export class PermissionManagementComponent
) )
.pipe( .pipe(
switchMap(() => 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)), finalize(() => (this.modalBusy = false)),
) )
@ -282,8 +294,8 @@ export class PermissionManagementComponent
} }
shouldFetchAppConfig() { shouldFetchAppConfig() {
const currentUser = this.store.selectSnapshot( const currentUser = this.configState.getOne(
ConfigState.getOne('currentUser'), 'currentUser',
) as ApplicationConfiguration.CurrentUser; ) as ApplicationConfiguration.CurrentUser;
if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey); if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey);

14
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 { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Select } from '@ngxs/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
@Component({ @Component({
@ -48,14 +47,19 @@ import { Observable } from 'rxjs';
`, `,
}) })
export class CurrentUserComponent implements OnInit { export class CurrentUserComponent implements OnInit {
@Select(ConfigState.getOne('currentUser')) currentUser$: Observable<ApplicationConfiguration.CurrentUser> = this.configState.getOne$(
currentUser$: Observable<ApplicationConfiguration.CurrentUser>; 'currentUser',
);
get smallScreen(): boolean { get smallScreen(): boolean {
return window.innerWidth < 992; return window.innerWidth < 992;
} }
constructor(private authService: AuthService, private router: Router) {} constructor(
private authService: AuthService,
private router: Router,
private configState: ConfigStateService,
) {}
ngOnInit() {} ngOnInit() {}

16
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 { ApplicationConfiguration, ConfigStateService, SessionStateService } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core'; import { Component } from '@angular/core';
import { Select } from '@ngxs/store';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import snq from 'snq'; import snq from 'snq';
@ -44,13 +43,14 @@ import snq from 'snq';
</div> </div>
`, `,
}) })
export class LanguagesComponent implements OnInit { export class LanguagesComponent {
get smallScreen(): boolean { get smallScreen(): boolean {
return window.innerWidth < 992; return window.innerWidth < 992;
} }
@Select(ConfigState.getDeep('localization.languages')) languages$: Observable<ApplicationConfiguration.Language[]> = this.configState.getDeep$(
languages$: Observable<ApplicationConfiguration.Language[]>; 'localization.languages',
);
get defaultLanguage$(): Observable<string> { get defaultLanguage$(): Observable<string> {
return this.languages$.pipe( return this.languages$.pipe(
@ -78,9 +78,7 @@ export class LanguagesComponent implements OnInit {
return this.sessionState.getLanguage(); return this.sessionState.getLanguage();
} }
constructor(private sessionState: SessionStateService) {} constructor(private sessionState: SessionStateService, private configState: ConfigStateService) {}
ngOnInit() {}
onChangeLang(cultureName: string) { onChangeLang(cultureName: string) {
this.sessionState.setLanguage(cultureName); this.sessionState.setLanguage(cultureName);

85
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 { ABP, ApplicationConfiguration, ConfigStateService } from '@abp/ng.core';
import { createSelector, Store } from '@ngxs/store';
import { Observable, pipe, zip } from 'rxjs'; import { Observable, pipe, zip } from 'rxjs';
import { filter, map, switchMap, take } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
import { ePropType } from '../enums/props.enum'; import { ePropType } from '../enums/props.enum';
@ -12,51 +11,59 @@ import { createDisplayNameLocalizationPipeKeyGenerator } from './localization.ut
import { createExtraPropertyValueResolver } from './props.util'; import { createExtraPropertyValueResolver } from './props.util';
import { getValidatorsFromProperty } from './validation.util'; import { getValidatorsFromProperty } from './validation.util';
const selectConfig = (state: any) => state.ConfigState; function selectObjectExtensions(
configState: ConfigStateService,
const selectObjectExtensions = createSelector( ): Observable<ObjectExtensions.Item> {
[selectConfig], return configState.getOne$('objectExtensions');
(config: ObjectExtensions.Config) => config.objectExtensions, }
);
function selectLocalization(
const selectLocalization = createSelector( configState: ConfigStateService,
[selectConfig], ): Observable<ApplicationConfiguration.Localization> {
(config: ApplicationConfiguration.Response) => config.localization, return configState.getOne$('localization');
); }
const selectEnums = createSelector( function selectEnums(
[selectObjectExtensions, selectLocalization], configState: ConfigStateService,
(extensions: ObjectExtensions.Item) => ): Observable<Record<string, ObjectExtensions.Enum>> {
Object.keys(extensions.enums).reduce((acc, key) => { return selectObjectExtensions(configState).pipe(
const { fields, localizationResource } = extensions.enums[key]; map((extensions: ObjectExtensions.Item) =>
acc[key] = { Object.keys(extensions.enums).reduce((acc, key) => {
fields, const { fields, localizationResource } = extensions.enums[key];
localizationResource, acc[key] = {
transformed: createEnum(fields), fields,
}; localizationResource,
return acc; transformed: createEnum(fields),
}, {} as ObjectExtensions.Enums), };
); 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(
}); configState: ConfigStateService,
moduleKey: ModuleKey,
export function getObjectExtensionEntitiesFromStore(store: Store, moduleKey: ModuleKey) { ) {
return store.select(createObjectExtensionEntitiesSelector(moduleKey)).pipe( return selectObjectExtensions(configState).pipe(
map(extensions => {
if (!extensions) return null;
return (extensions.modules[moduleKey] || ({} as ObjectExtensions.Module)).entities;
}),
map(entities => (isUndefined(entities) ? {} : entities)), map(entities => (isUndefined(entities) ? {} : entities)),
filter<ObjectExtensions.Entities>(Boolean), filter<ObjectExtensions.Entities>(Boolean),
take(1), take(1),
); );
} }
export function mapEntitiesToContributors<T = any>(store: Store, resource: string) { export function mapEntitiesToContributors<T = any>(
configState: ConfigStateService,
resource: string,
) {
return pipe( return pipe(
switchMap(entities => switchMap(entities =>
zip(store.select(selectLocalization), store.select(selectEnums)).pipe( zip(selectLocalization(configState), selectEnums(configState)).pipe(
map(([localization, enums]) => { map(([localization, enums]) => {
const generateDisplayName = createDisplayNameLocalizationPipeKeyGenerator(localization); const generateDisplayName = createDisplayNameLocalizationPipeKeyGenerator(localization);

9
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 { DatePipe } from '@angular/common';
import { Injectable, Optional } from '@angular/core'; import { Injectable, Optional } from '@angular/core';
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { Store } from '@ngxs/store';
function padNumber(value: number) { function padNumber(value: number) {
if (isNumber(value)) { if (isNumber(value)) {
@ -22,7 +21,7 @@ function toInteger(value: any): number {
@Injectable() @Injectable()
export class DateParserFormatter extends NgbDateParserFormatter { export class DateParserFormatter extends NgbDateParserFormatter {
constructor(@Optional() private datePipe: DatePipe, private store: Store) { constructor(@Optional() private datePipe: DatePipe, private configState: ConfigStateService) {
super(); super();
} }
@ -50,8 +49,8 @@ export class DateParserFormatter extends NgbDateParserFormatter {
} }
format(date: NgbDateStruct): string { format(date: NgbDateStruct): string {
const { shortDatePattern } = (this.store.selectSnapshot( const { shortDatePattern } = (this.configState.getOne(
ConfigState.getOne('localization'), 'localization',
) as ApplicationConfiguration.Localization).currentCulture.dateTimeFormat; ) as ApplicationConfiguration.Localization).currentCulture.dateTimeFormat;
if (date && this.datePipe) { if (date && this.datePipe) {

Loading…
Cancel
Save