import { EMPTY, Observable, ReplaySubject, Subject, Subscription } from "rxjs";
import { filter, map, tap } from "rxjs/operators";
import { Injectable, Optional } from "@angular/core";
import {
ActivatedRoute,
ActivationEnd,
ChildActivationEnd,
Router,
} from "@angular/router";
import { CurrentLocaleService } from "../../components/i18n/currentLocale.service";
import { HalService } from "../../components/hal/hal.service";
import { Selectors } from "../../components/app-context/api";
import { AppContext } from "../../components/app-context/app.context";
export interface GlobalChannel {
channel: string;
property?: string | any;
}
export interface SourceChannelDefinition {
factory?: (channel: GlobalChannel) => Observable<any>;
observable?: Observable<any>;
mapper?: (data: any, name: string) => any;
}
export class GlobalChannelRegistry {
constructor(
private sources: Map<string, SourceChannelDefinition> = new Map(),
private targets: Map<string, Subject<any>> = new Map()
) {}
public registerTarget(name: string, subject: Subject<any>): void {
this.targets.set(name, subject);
}
public registerSource(
name: string,
observable: Observable<any>,
mapper?: (data: any, property: string) => any
): void {
this.sources.set(name, {
observable: observable,
mapper: mapper,
});
}
public registerSourceFactory(
name: string,
factory: (channel: GlobalChannel) => Observable<any>
): void {
this.sources.set(name, {
factory: factory,
});
}
public getTarget(channel: GlobalChannel): Subject<any> {
return this.targets.get(channel.channel);
}
public getSource(channel: GlobalChannel): Observable<any> {
let definition = this.sources.get(channel.channel);
if (definition.observable) {
let observable = definition.observable;
if (
channel.property &&
typeof channel.property == "string" &&
definition.mapper
) {
return observable.pipe(
map((data) => definition.mapper(data, channel.property))
);
}
return observable;
}
if (definition.factory) {
return definition.factory(channel);
}
return EMPTY;
}
public clone(): GlobalChannelRegistry {
return new GlobalChannelRegistry(
new Map(this.sources),
new Map(this.targets)
);
}
}
export type EndEvent = ActivationEnd | ChildActivationEnd;
interface RouteParams {
queryParams: { [key: string]: string };
params: { [key: string]: string };
}
@Injectable()
export class AppGlobalChannelsService extends GlobalChannelRegistry {
private queryParamsSub: Subscription;
constructor(
localeService: CurrentLocaleService,
halService: HalService,
router: Router,
activatedRoute: ActivatedRoute,
@Optional() appContext: AppContext
) {
super();
let routes = new ReplaySubject<RouteParams>(1);
router.events
.pipe(
filter((event) => event instanceof ActivationEnd),
map((event) => {
let result: RouteParams = {
queryParams: {},
params: {},
};
let route = (event as ActivationEnd).snapshot;
while (route) {
result.queryParams = Object.assign(
{},
result.queryParams,
route.queryParams
);
result.params = Object.assign({}, result.params, route.params);
route = route.firstChild;
}
return result;
})
)
.subscribe(routes);
this.registerSource("locale", localeService.getCurrentLocale());
this.registerSource("action", halService.getActionEvents());
this.registerSource(
"query-params",
routes.pipe(map((route) => route.queryParams)),
(params, prop) => (prop ? params[prop] : params)
);
this.registerSource(
"params",
routes.pipe(map((route) => route.params)),
(params, prop) => (prop ? params[prop] : params)
);
let queryParamsSubject = new Subject<{ [key: string]: string }>();
this.queryParamsSub = queryParamsSubject.subscribe((queryParams) => {
router.navigate([], {
relativeTo: activatedRoute,
queryParams,
queryParamsHandling: "merge",
});
});
this.registerTarget("query-params", queryParamsSubject);
this.registerSourceFactory("toolbox-context", (channel) => {
let selectors: Selectors = {};
if (channel.property && typeof channel.property == "object") {
selectors = <Selectors>channel.property;
}
return appContext.userContext.subscribe(selectors);
});
}
}