wip migrations
This commit is contained in:
parent
36cc0cff3a
commit
3bd45d958d
@ -1,75 +0,0 @@
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { inject, ModuleWithProviders, NgModule, Optional, Provider, SkipSelf } from '@angular/core';
|
||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { ApiPathInterceptor, DefaultUserPreferenceService, IqserConfigService, IqserUserPreferenceService } from './services';
|
||||
import { CommonUiOptions, IqserAppConfig, ModuleOptions } from './utils';
|
||||
import { ICONS } from './utils/constants';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
multi: true,
|
||||
useClass: ApiPathInterceptor,
|
||||
},
|
||||
{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'outline' } },
|
||||
],
|
||||
})
|
||||
export class CommonUiModule {
|
||||
constructor(@Optional() @SkipSelf() parentModule?: CommonUiModule) {
|
||||
if (parentModule) {
|
||||
throw new Error('CommonUiModule is already loaded. Import it in the AppModule only!');
|
||||
}
|
||||
|
||||
const iconRegistry = inject(MatIconRegistry);
|
||||
const sanitizer = inject(DomSanitizer);
|
||||
|
||||
ICONS.forEach(icon => {
|
||||
const url = sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`);
|
||||
iconRegistry.addSvgIconInNamespace('iqser', icon, url);
|
||||
});
|
||||
}
|
||||
|
||||
static forRoot<
|
||||
UserPreference extends IqserUserPreferenceService,
|
||||
Config extends IqserConfigService<AppConfig>,
|
||||
AppConfig extends IqserAppConfig = IqserAppConfig,
|
||||
>(options: CommonUiOptions<UserPreference, Config, AppConfig>): ModuleWithProviders<CommonUiModule> {
|
||||
const userPreferenceService = ModuleOptions.getService(
|
||||
IqserUserPreferenceService,
|
||||
DefaultUserPreferenceService,
|
||||
options.existingUserPreferenceService,
|
||||
);
|
||||
|
||||
const configServiceProviders = this._getConfigServiceProviders(options.configServiceFactory, options.configService);
|
||||
|
||||
return {
|
||||
ngModule: CommonUiModule,
|
||||
providers: [userPreferenceService, ...configServiceProviders],
|
||||
};
|
||||
}
|
||||
|
||||
private static _getConfigServiceProviders(configServiceFactory: () => unknown, configService?: unknown): Provider[] {
|
||||
if (configService) {
|
||||
return [
|
||||
{
|
||||
provide: configService,
|
||||
useFactory: configServiceFactory,
|
||||
},
|
||||
{
|
||||
provide: IqserConfigService,
|
||||
useExisting: configService,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
provide: IqserConfigService,
|
||||
useFactory: configServiceFactory,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -10,10 +10,11 @@ import {
|
||||
import { Inject, Injectable, Optional } from '@angular/core';
|
||||
import { finalize, MonoTypeOperatorFunction, Observable, retry, throwError, timer } from 'rxjs';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
import { MAX_RETRIES_ON_SERVER_ERROR, SERVER_ERROR_SKIP_PATHS } from './tokens';
|
||||
import { ErrorService } from './error.service';
|
||||
import { KeycloakStatusService } from '../tenants';
|
||||
import { LoadingService } from '../loading';
|
||||
import { getConfig } from '../services';
|
||||
import { KeycloakStatusService } from '../tenants';
|
||||
import { ErrorService } from './error.service';
|
||||
import { SERVER_ERROR_SKIP_PATHS } from './tokens';
|
||||
|
||||
function updateSeconds(seconds: number) {
|
||||
if (seconds === 0 || seconds === 1) {
|
||||
@ -52,12 +53,12 @@ function backoffOnServerError(maxRetries = 3, skippedPaths: string[] = []): Mono
|
||||
@Injectable()
|
||||
export class ServerErrorInterceptor implements HttpInterceptor {
|
||||
private readonly _urlsWithError = new Set();
|
||||
readonly #config = getConfig();
|
||||
|
||||
constructor(
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _keycloakStatusService: KeycloakStatusService,
|
||||
@Optional() @Inject(MAX_RETRIES_ON_SERVER_ERROR) private readonly _maxRetries: number,
|
||||
@Optional() @Inject(SERVER_ERROR_SKIP_PATHS) private readonly _skippedPaths: string[],
|
||||
) {}
|
||||
|
||||
@ -88,7 +89,7 @@ export class ServerErrorInterceptor implements HttpInterceptor {
|
||||
}),
|
||||
);
|
||||
}),
|
||||
backoffOnServerError(this._maxRetries, this._skippedPaths),
|
||||
backoffOnServerError(this.#config.MAX_RETRIES_ON_SERVER_ERROR, this._skippedPaths),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const MAX_RETRIES_ON_SERVER_ERROR = new InjectionToken<number>('Number of retries before giving up');
|
||||
export const SERVER_ERROR_SKIP_PATHS = new InjectionToken<string[]>('A list of paths to skip when handling server errors');
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
||||
import { getConfig } from '../services';
|
||||
import { IqserUserPreferenceService } from '../services';
|
||||
import { getConfig, IqserUserPreferenceService } from '../services';
|
||||
import { HelpModeDialogComponent } from './help-mode-dialog/help-mode-dialog.component';
|
||||
import { HELP_MODE_KEYS, MANUAL_BASE_URL } from './tokens';
|
||||
import { HELP_MODE_KEYS } from './tokens';
|
||||
import { HelpModeKey } from './types';
|
||||
import {
|
||||
DOCUMINE_THEME_CLASS,
|
||||
@ -20,7 +20,6 @@ import {
|
||||
ScrollableParentViews,
|
||||
WEB_VIEWER_ELEMENTS,
|
||||
} from './utils/constants';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
||||
export interface Helper {
|
||||
readonly element: HTMLElement;
|
||||
@ -39,13 +38,13 @@ export class HelpModeService {
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
#helpers: Record<string, Helper> = {};
|
||||
#dialogMode = false;
|
||||
readonly #config = getConfig();
|
||||
readonly isHelpModeActive$ = this.#isHelpModeActive$.asObservable();
|
||||
readonly isHelpModeActive = toSignal(this.isHelpModeActive$, { initialValue: false });
|
||||
readonly helpModeDialogIsOpened$ = this.#helpModeDialogIsOpened$.asObservable();
|
||||
|
||||
constructor(
|
||||
@Inject(HELP_MODE_KEYS) private readonly _keys: HelpModeKey[],
|
||||
@Inject(MANUAL_BASE_URL) private readonly _manualBaseURL: string,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _rendererFactory: RendererFactory2,
|
||||
private readonly _translateService: TranslateService,
|
||||
@ -161,7 +160,7 @@ export class HelpModeService {
|
||||
|
||||
#generateDocsLink(key: string) {
|
||||
const currentLang = this._translateService.currentLang;
|
||||
return `${this._manualBaseURL}/${currentLang}/index-${currentLang}.html?contextId=${key}`;
|
||||
return `${this.#config.MANUAL_BASE_URL}/${currentLang}/index-${currentLang}.html?contextId=${key}`;
|
||||
}
|
||||
|
||||
#isElementVisible(helper: Helper): boolean {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import { inject, InjectionToken } from '@angular/core';
|
||||
import { IqserConfigService } from '../services/iqser-config.service';
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { HelpModeKey } from './types';
|
||||
|
||||
export const HELP_MODE_KEYS = new InjectionToken<HelpModeKey>('Help mode keys');
|
||||
export const MANUAL_BASE_URL = new InjectionToken<string>('Base manual URL', {
|
||||
factory: () => inject(IqserConfigService).values.MANUAL_BASE_URL,
|
||||
});
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { makeEnvironmentProviders } from '@angular/core';
|
||||
import { HelpModeService } from '../help-mode.service';
|
||||
import { HELP_MODE_KEYS } from '../tokens';
|
||||
import { HelpModeKey } from '../types';
|
||||
|
||||
export function provideHelpMode(helpModeKeys: HelpModeKey[]) {
|
||||
return [{ provide: HELP_MODE_KEYS, useValue: helpModeKeys }, HelpModeService];
|
||||
return makeEnvironmentProviders([{ provide: HELP_MODE_KEYS, useValue: helpModeKeys }, HelpModeService]);
|
||||
}
|
||||
|
||||
17
src/lib/interceptors/api-path.interceptor.ts
Normal file
17
src/lib/interceptors/api-path.interceptor.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { HttpHandlerFn, HttpInterceptorFn, HttpRequest } from '@angular/common/http';
|
||||
import { inject } from '@angular/core';
|
||||
import { getConfig } from '../services/iqser-config.service';
|
||||
import { UI_ROOT_PATH_FN } from '../utils/tokens';
|
||||
|
||||
export const apiPathInterceptorFn: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
|
||||
const config = getConfig();
|
||||
const convertPath = inject(UI_ROOT_PATH_FN);
|
||||
|
||||
if (!req.url.startsWith('/assets')) {
|
||||
const apiUrl = `${config.API_URL}${req.url}`;
|
||||
return next(req.clone({ url: apiUrl }));
|
||||
}
|
||||
|
||||
const url = convertPath(req.url);
|
||||
return next(req.clone({ url }));
|
||||
};
|
||||
49
src/lib/provide-common-ui.ts
Normal file
49
src/lib/provide-common-ui.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
||||
import { EnvironmentProviders, inject, makeEnvironmentProviders, provideAppInitializer, Provider, Type } from '@angular/core';
|
||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { apiPathInterceptorFn } from './interceptors/api-path.interceptor';
|
||||
import { CONFIG_SERVICE, DefaultUserPreferenceService, IqserConfigService, IqserUserPreferenceService } from './services';
|
||||
import { ICONS } from './utils/constants';
|
||||
|
||||
type ProvideCommonUiOptions = {
|
||||
configService: Type<IqserConfigService>;
|
||||
existingUserPreferenceService: Type<IqserUserPreferenceService>;
|
||||
};
|
||||
|
||||
export function provideCommonUi(options: ProvideCommonUiOptions): EnvironmentProviders {
|
||||
return makeEnvironmentProviders([
|
||||
provideCommonIcons(),
|
||||
{
|
||||
provide: CONFIG_SERVICE,
|
||||
useExisting: options.configService,
|
||||
},
|
||||
provideHttpClient(withInterceptors([apiPathInterceptorFn])),
|
||||
{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'outline' } },
|
||||
getService(IqserUserPreferenceService, DefaultUserPreferenceService, options.existingUserPreferenceService),
|
||||
]);
|
||||
}
|
||||
|
||||
export function getService<B, D, E>(base: B, _default: Type<D>, existing?: E): Provider {
|
||||
if (existing) {
|
||||
return {
|
||||
provide: base,
|
||||
useExisting: existing,
|
||||
};
|
||||
}
|
||||
|
||||
return { provide: base, useClass: _default };
|
||||
}
|
||||
|
||||
function provideCommonIcons() {
|
||||
return provideAppInitializer(() => {
|
||||
const iconRegistry = inject(MatIconRegistry);
|
||||
const sanitizer = inject(DomSanitizer);
|
||||
|
||||
ICONS.forEach(icon => {
|
||||
const url = sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`);
|
||||
iconRegistry.addSvgIconInNamespace('iqser', icon, url);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { UI_ROOT_PATH_FN } from '../utils/tokens';
|
||||
import { getConfig } from './iqser-config.service';
|
||||
|
||||
@Injectable()
|
||||
export class ApiPathInterceptor implements HttpInterceptor {
|
||||
readonly #config = getConfig();
|
||||
readonly #convertPath = inject(UI_ROOT_PATH_FN);
|
||||
|
||||
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
if (!req.url.startsWith('/assets')) {
|
||||
const apiUrl = `${this.#config.API_URL}${req.url}`;
|
||||
return next.handle(req.clone({ url: apiUrl }));
|
||||
}
|
||||
|
||||
const url = this.#convertPath(req.url);
|
||||
return next.handle(req.clone({ url }));
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,3 @@
|
||||
import exp from 'constants';
|
||||
|
||||
export * from './toaster.service';
|
||||
export * from './error-message.service';
|
||||
export * from './generic.service';
|
||||
@ -9,5 +7,4 @@ export * from './entities-map.service';
|
||||
export * from './iqser-user-preference.service';
|
||||
export * from './language.service';
|
||||
export * from './iqser-config.service';
|
||||
export * from './api-path.interceptor';
|
||||
export * from './skeleton.service';
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { Inject, inject, Injectable } from '@angular/core';
|
||||
import { inject, InjectionToken } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { CacheApiService } from '../caching/cache-api.service';
|
||||
import { wipeAllCaches } from '../caching/cache-utils';
|
||||
import { IqserAppConfig } from '../utils/iqser-app-config';
|
||||
import { LANDING_PAGE_THEMES, MANUAL_BASE_URL, THEME_DIRECTORIES } from '../utils/constants';
|
||||
import { IStoredTenantId, TenantsService } from '../tenants';
|
||||
import { LANDING_PAGE_THEMES, MANUAL_BASE_URL, THEME_DIRECTORIES } from '../utils/constants';
|
||||
import { IqserAppConfig } from '../utils/iqser-app-config';
|
||||
|
||||
@Injectable()
|
||||
export class IqserConfigService<T extends IqserAppConfig = IqserAppConfig> {
|
||||
export const CONFIG_SERVICE = new InjectionToken<IqserConfigService>('IqserConfigService');
|
||||
|
||||
export abstract class IqserConfigService<T extends IqserAppConfig = IqserAppConfig> {
|
||||
protected readonly _cacheApiService = inject(CacheApiService);
|
||||
protected readonly _titleService = inject(Title);
|
||||
protected readonly _tenantsService = inject(TenantsService);
|
||||
|
||||
constructor(@Inject('Doesnt matter') protected _values: T) {
|
||||
protected constructor(protected _values: T) {
|
||||
this._checkFrontendVersion();
|
||||
this.#updateAppType();
|
||||
this._titleService.setTitle(this._values.APP_NAME);
|
||||
@ -73,5 +74,5 @@ export class IqserConfigService<T extends IqserAppConfig = IqserAppConfig> {
|
||||
}
|
||||
|
||||
export function getConfig<T extends IqserAppConfig = IqserAppConfig>() {
|
||||
return inject<IqserConfigService<T>>(IqserConfigService).values;
|
||||
return inject<IqserConfigService<T>>(CONFIG_SERVICE).values;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export interface IqserTranslateModuleOptions {
|
||||
readonly pathPrefix?: string;
|
||||
readonly pathPrefixFactory?: () => string;
|
||||
}
|
||||
|
||||
@ -45,7 +45,10 @@ export class IqserTranslateModule {
|
||||
providers: [
|
||||
{
|
||||
provide: translateLoaderToken,
|
||||
useFactory: () => pruningTranslationLoaderFactory(pathPrefix),
|
||||
useFactory: () => {
|
||||
const prefix = options?.pathPrefixFactory !== undefined ? options.pathPrefixFactory() : pathPrefix;
|
||||
return pruningTranslationLoaderFactory(prefix);
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: MissingTranslationHandler,
|
||||
|
||||
@ -17,4 +17,3 @@ export * from './context.component';
|
||||
export * from './tokens';
|
||||
export * from './module-options';
|
||||
export * from './iqser-app-config';
|
||||
export * from './types/common-ui-options';
|
||||
|
||||
@ -9,4 +9,5 @@ export interface IqserAppConfig {
|
||||
readonly MANUAL_BASE_URL: string;
|
||||
readonly BASE_TRANSLATIONS_DIRECTORY?: string;
|
||||
readonly LANDING_PAGE_THEME: 'redactmanager' | 'documine' | 'mixed';
|
||||
readonly MAX_RETRIES_ON_SERVER_ERROR?: number;
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { Type } from '@angular/core';
|
||||
import { IqserConfigService } from '../../services/iqser-config.service';
|
||||
import { IqserUserPreferenceService } from '../../services/iqser-user-preference.service';
|
||||
import { IqserAppConfig } from '../iqser-app-config';
|
||||
|
||||
export interface CommonUiOptions<
|
||||
UserPreference extends IqserUserPreferenceService,
|
||||
Config extends IqserConfigService<AppConfig>,
|
||||
AppConfig extends IqserAppConfig,
|
||||
> {
|
||||
existingUserPreferenceService?: Type<UserPreference>;
|
||||
configService?: Type<Config>;
|
||||
configServiceFactory: () => Config;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user