neweb
Version:
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Coverage percentage][coveralls-image]][coveralls-url] [ • 6.22 kB
text/typescript
import debug = require("debug");
import { Component, hydrate, replace } from "neweb-components";
import {
IPage, IPageFrame,
IRemoteFrameControllerDataParams, IRemoteFrameControllerDispatchParams,
} from "neweb-core";
import { Onemitter } from "onemitter";
import { BehaviorSubject } from "rxjs/BehaviorSubject";
import { Subject } from "rxjs/Subject";
import { ISeanceContext, NavigateStatus, NetworkStatus } from "./../../ISeanceContext";
import { IViewProps } from "./IViewProps";
export interface IClientPageRendererConfig {
rootHtmlElement: HTMLElement;
app: {
getFrameViewClass: (pageFrame: IPageFrame) => any;
};
}
class ClientPageRenderer {
protected navigate: (url: string) => void;
protected dispatch: (params: IRemoteFrameControllerDispatchParams) => Promise<void>;
protected seansStatusEmitter: Onemitter<string>;
protected networkStatusEmitter: Onemitter<string>;
protected historyContext: any;
protected seanceContext: ISeanceContext;
protected views: { [index: string]: new (config: IViewProps<any, any, any>) => Component<any> } = {};
protected frames: {
[index: string]: {
component: Component<any>;
data: { [index: string]: Subject<any> };
children: BehaviorSubject<{ [index: string]: Component<any> }>;
params: BehaviorSubject<any>;
pageFrame: IPageFrame;
};
} = {};
protected currentPage: IPage;
constructor(protected config: IClientPageRendererConfig) { }
public setMethods(params: {
navigate: (url: string) => void;
dispatch: (params: IRemoteFrameControllerDispatchParams) => Promise<void>;
seansStatusEmitter: Onemitter<any>;
networkStatusEmitter: Onemitter<any>;
historyContext: any;
}) {
this.navigate = params.navigate;
this.dispatch = params.dispatch;
this.seansStatusEmitter = params.seansStatusEmitter;
this.networkStatusEmitter = params.networkStatusEmitter;
this.historyContext = params.historyContext;
const networkStatus = new BehaviorSubject<NetworkStatus>("" as any);
const navigateStatus = new BehaviorSubject<NavigateStatus>("" as any);
this.networkStatusEmitter.onAndGet((value) => {
networkStatus.next(value as any);
});
this.seansStatusEmitter.onAndGet((value) => {
navigateStatus.next(value as any);
});
this.seanceContext = {
navigate: this.navigate,
networkStatus,
navigateStatus,
};
}
public async loadPage(page: IPage) {
await this.loadViews(page);
// create all frames
page.frames.map((pageFrame) => {
this.frames[pageFrame.frameId] = this.createFrame(pageFrame);
});
this.renderFrame(page.rootFrame, page);
this.currentPage = page;
}
public async newPage(page: IPage) {
debug("neweb:renderer")("new page", page);
await this.loadViews(page);
const frameIds: string[] = [];
page.frames.map(async (frame) => {
if (!this.frames[frame.frameId]) {
this.frames[frame.frameId] = this.createFrame(frame);
frameIds.push(frame.frameId);
} else {
const xFrame = this.frames[frame.frameId];
if (JSON.stringify(xFrame.params.getValue()) !== frame.params) {
xFrame.params.next(frame.params);
}
}
});
// frameIds.map((frameId) => this.renderFrame(frameId, page));
this.renderFrame(page.rootFrame, page);
// TODO delete old frames
if (this.currentPage.rootFrame !== page.rootFrame) {
replace(this.frames[page.rootFrame].component, this.config.rootHtmlElement);
}
this.currentPage = page;
}
public async initialize() {
hydrate(this.frames[this.currentPage.rootFrame].component, this.config.rootHtmlElement);
}
public emitFrameControllerData(params: IRemoteFrameControllerDataParams) {
const frame = this.frames[params.frameId];
if (frame) {
frame.data[params.fieldName].next(params.value);
}
}
protected async loadViews(page: IPage) {
await Promise.all(page.frames.map(async (pageFrame) => {
this.views[pageFrame.frameName] = await this.config.app.getFrameViewClass(pageFrame);
}));
}
protected renderFrame(frameId: string, page: IPage) {
const frame = this.frames[frameId];
const pageFrame = page.frames.filter((f) => f.frameId === frameId)[0];
const children: { [index: string]: Component<any> } = {};
Object.keys(pageFrame.frames).map((placeName) => {
const childFrameId = pageFrame.frames[placeName];
this.renderFrame(childFrameId, page);
const childFrame = this.frames[childFrameId];
children[placeName] = childFrame.component;
});
frame.children.next(children);
}
protected createFrame(pageFrame: IPageFrame) {
const ViewClass = this.views[pageFrame.frameName];
const data: { [index: string]: Subject<any> } = {};
Object.keys(pageFrame.data).map((dataName) => {
data[dataName] = new BehaviorSubject(pageFrame.data[dataName]);
});
const children: BehaviorSubject<{ [index: string]: Component<any> }> = new BehaviorSubject({});
const params = new BehaviorSubject(pageFrame.params);
const component = new ViewClass({
data,
children,
params,
seance: this.seanceContext,
dispatch: (actionName: string, ...args: any[]) => this.dispatch({
frameId: pageFrame.frameId,
actionName,
args,
}),
});
return {
pageFrame,
component,
data,
children,
params,
};
}
}
export default ClientPageRenderer;