UNPKG

5.75 kBPlain TextView Raw
1import debug = require("debug");
2import { Component, hydrate, replace } from "neweb-components";
3import {
4 IPage, IPageFrame,
5 IRemoteFrameControllerDataParams, IRemoteFrameControllerDispatchParams,
6} from "neweb-core";
7import { Onemitter } from "onemitter";
8import { BehaviorSubject } from "rxjs/BehaviorSubject";
9import { Subject } from "rxjs/Subject";
10import { IViewParams } from "./IViewParams";
11export interface IClientPageRendererConfig {
12 rootHtmlElement: HTMLElement;
13 app: {
14 getFrameViewClass: (pageFrame: IPageFrame) => any;
15 };
16}
17class ClientPageRenderer {
18 protected navigate: (url: string) => void;
19 protected dispatch: (params: IRemoteFrameControllerDispatchParams) => Promise<void>;
20 protected seansStatusEmitter: Onemitter<string>;
21 protected networkStatusEmitter: Onemitter<string>;
22 protected historyContext: any;
23 protected views: { [index: string]: new (config: IViewParams<any, any, any>) => Component<any> } = {};
24 protected frames: {
25 [index: string]: {
26 component: Component<any>;
27 data: { [index: string]: Subject<any> };
28 children: { [index: string]: BehaviorSubject<Component<any>> };
29 params: BehaviorSubject<any>;
30 pageFrame: IPageFrame;
31 };
32 } = {};
33 protected currentPage: IPage;
34 constructor(protected config: IClientPageRendererConfig) { }
35 public setMethods(params: {
36 navigate: (url: string) => void;
37 dispatch: (params: IRemoteFrameControllerDispatchParams) => Promise<void>;
38 seansStatusEmitter: Onemitter<any>;
39 networkStatusEmitter: Onemitter<any>;
40 historyContext: any;
41 }) {
42 this.navigate = params.navigate;
43 this.dispatch = params.dispatch;
44 this.seansStatusEmitter = params.seansStatusEmitter;
45 this.networkStatusEmitter = params.networkStatusEmitter;
46 this.historyContext = params.historyContext;
47 }
48 public async loadPage(page: IPage) {
49 await this.loadViews(page);
50 // create all frames
51 page.frames.map((pageFrame) => {
52 this.frames[pageFrame.frameId] = this.createFrame(pageFrame);
53 });
54 this.renderFrame(page.rootFrame, page);
55 this.currentPage = page;
56 }
57 public async newPage(page: IPage) {
58 debug("neweb:renderer")("new page", page);
59 await this.loadViews(page);
60 const frameIds: string[] = [];
61 page.frames.map(async (frame) => {
62 if (!this.frames[frame.frameId]) {
63 this.frames[frame.frameId] = this.createFrame(frame);
64 frameIds.push(frame.frameId);
65 } else {
66 const xFrame = this.frames[frame.frameId];
67 if (JSON.stringify(xFrame.params.getValue()) !== frame.params) {
68 xFrame.params.next(frame.params);
69 }
70 }
71 });
72 // frameIds.map((frameId) => this.renderFrame(frameId, page));
73 this.renderFrame(page.rootFrame, page);
74 // TODO delete old frames
75 if (this.currentPage.rootFrame !== page.rootFrame) {
76 replace(this.frames[page.rootFrame].component, this.config.rootHtmlElement);
77 }
78 this.currentPage = page;
79 }
80 public async initialize() {
81 hydrate(this.frames[this.currentPage.rootFrame].component, this.config.rootHtmlElement);
82 }
83 public emitFrameControllerData(params: IRemoteFrameControllerDataParams) {
84 const frame = this.frames[params.frameId];
85 if (frame) {
86 frame.data[params.fieldName].next(params.value);
87 }
88 }
89 protected async loadViews(page: IPage) {
90 await Promise.all(page.frames.map(async (pageFrame) => {
91 this.views[pageFrame.frameName] = await this.config.app.getFrameViewClass(pageFrame);
92 }));
93 }
94 protected renderFrame(frameId: string, page: IPage) {
95 const frame = this.frames[frameId];
96 const pageFrame = page.frames.filter((f) => f.frameId === frameId)[0];
97 Object.keys(pageFrame.frames).map((placeName) => {
98 const childFrameId = pageFrame.frames[placeName];
99 this.renderFrame(childFrameId, page);
100 const childFrame = this.frames[childFrameId];
101 if (frame.pageFrame.frames[placeName] !== pageFrame.frames[placeName]
102 || !frame.children[placeName].getValue()) {
103 frame.children[placeName].next(childFrame.component);
104 }
105 });
106 }
107 protected createFrame(pageFrame: IPageFrame) {
108 const ViewClass = this.views[pageFrame.frameName];
109 const data: { [index: string]: Subject<any> } = {};
110 Object.keys(pageFrame.data).map((dataName) => {
111 data[dataName] = new BehaviorSubject(pageFrame.data[dataName]);
112 });
113 const children: { [index: string]: BehaviorSubject<Component<any>> } = {};
114 Object.keys(pageFrame.frames).map((childName) => {
115 children[childName] = new BehaviorSubject(undefined as any);
116 });
117 const params = new BehaviorSubject(pageFrame.params);
118 const component = new ViewClass({
119 data,
120 children,
121 params,
122 navigate: this.navigate,
123 dispatch: (actionName: string, ...args: any[]) => this.dispatch({
124 frameId: pageFrame.frameId,
125 actionName,
126 args,
127 }),
128 });
129 return {
130 pageFrame,
131 component,
132 data,
133 children,
134 params,
135 };
136 }
137}
138export default ClientPageRenderer;