Showing
16 changed files
with
165 additions
and
93 deletions
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | "main": "src/main.ts", |
23 | 23 | "polyfills": "src/polyfills.ts", |
24 | 24 | "tsConfig": "src/tsconfig.app.json", |
25 | + "aot": true, | |
25 | 26 | "assets": [ |
26 | 27 | "src/thingsboard.ico", |
27 | 28 | "src/assets", |
... | ... | @@ -161,6 +162,7 @@ |
161 | 162 | "serve": { |
162 | 163 | "builder": "@angular-builders/custom-webpack:dev-server", |
163 | 164 | "options": { |
165 | + "aot": true, | |
164 | 166 | "browserTarget": "thingsboard:build", |
165 | 167 | "proxyConfig": "proxy.conf.js" |
166 | 168 | }, | ... | ... |
... | ... | @@ -14,8 +14,10 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | const CompressionPlugin = require("compression-webpack-plugin"); |
17 | +const TerserPlugin = require("terser-webpack-plugin"); | |
17 | 18 | const webpack = require("webpack"); |
18 | 19 | const dirTree = require("directory-tree"); |
20 | +const AngularCompilerPlugin = require('@ngtools/webpack'); | |
19 | 21 | |
20 | 22 | var langs = []; |
21 | 23 | |
... | ... | @@ -25,12 +27,14 @@ dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => { |
25 | 27 | langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5)); |
26 | 28 | }); |
27 | 29 | |
28 | -module.exports = { | |
29 | - plugins: [ | |
30 | +module.exports = (config, options) => { | |
31 | + config.plugins.push( | |
30 | 32 | new webpack.DefinePlugin({ |
31 | 33 | TB_VERSION: JSON.stringify(require("./package.json").version), |
32 | 34 | SUPPORTED_LANGS: JSON.stringify(langs), |
33 | - }), | |
35 | + }) | |
36 | + ); | |
37 | + config.plugins.push( | |
34 | 38 | new CompressionPlugin({ |
35 | 39 | filename: "[path][base].gz[query]", |
36 | 40 | algorithm: "gzip", |
... | ... | @@ -38,6 +42,21 @@ module.exports = { |
38 | 42 | threshold: 10240, |
39 | 43 | minRatio: 0.8, |
40 | 44 | deleteOriginalAssets: false, |
41 | - }), | |
42 | - ], | |
45 | + }) | |
46 | + ); | |
47 | + | |
48 | + if (config.mode === 'production') { | |
49 | + const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin); | |
50 | + const angularCompilerOptions = config.plugins[index]._options; | |
51 | + angularCompilerOptions.emitClassMetadata = true; | |
52 | + angularCompilerOptions.emitNgModuleScope = true; | |
53 | + config.plugins.splice(index, 1); | |
54 | + config.plugins.push(new AngularCompilerPlugin.AngularCompilerPlugin(angularCompilerOptions)); | |
55 | + const terserPluginOptions = config.optimization.minimizer[1].options; | |
56 | + delete terserPluginOptions.terserOptions.compress.global_defs.ngJitMode; | |
57 | + terserPluginOptions.terserOptions.compress.side_effects = false; | |
58 | + config.optimization.minimizer.splice(1, 1); | |
59 | + config.optimization.minimizer.push(new TerserPlugin(terserPluginOptions)); | |
60 | + } | |
61 | + return config; | |
43 | 62 | }; | ... | ... |
... | ... | @@ -100,6 +100,7 @@ |
100 | 100 | "@angular/cli": "^10.1.5", |
101 | 101 | "@angular/compiler-cli": "^10.1.5", |
102 | 102 | "@angular/language-service": "^10.1.5", |
103 | + "@ngtools/webpack": "10.1.5", | |
103 | 104 | "@types/canvas-gauges": "^2.1.2", |
104 | 105 | "@types/flot": "^0.0.31", |
105 | 106 | "@types/jasmine": "^3.5.12", | ... | ... |
... | ... | @@ -446,8 +446,12 @@ export class AuthService { |
446 | 446 | const refreshTokenValid = AuthService.isTokenValid('refresh_token'); |
447 | 447 | this.setUserFromJwtToken(null, null, false); |
448 | 448 | if (!refreshTokenValid) { |
449 | - this.refreshTokenSubject.error(new Error(this.translate.instant('access.refresh-token-expired'))); | |
450 | - this.refreshTokenSubject = null; | |
449 | + this.translate.get('access.refresh-token-expired').subscribe( | |
450 | + (translation) => { | |
451 | + this.refreshTokenSubject.error(new Error(translation)); | |
452 | + this.refreshTokenSubject = null; | |
453 | + } | |
454 | + ); | |
451 | 455 | } else { |
452 | 456 | const refreshTokenRequest = { |
453 | 457 | refreshToken | ... | ... |
... | ... | @@ -59,37 +59,40 @@ export class DynamicComponentFactoryService { |
59 | 59 | template: string, |
60 | 60 | modules?: Type<any>[]): Observable<ComponentFactory<T>> { |
61 | 61 | const dymamicComponentFactorySubject = new ReplaySubject<ComponentFactory<T>>(); |
62 | - const comp = this.createDynamicComponent(componentType, template); | |
63 | - let moduleImports: Type<any>[] = [CommonModule]; | |
64 | - if (modules) { | |
65 | - moduleImports = [...moduleImports, ...modules]; | |
66 | - } | |
67 | - // noinspection AngularInvalidImportedOrDeclaredSymbol | |
68 | - @NgModule({ | |
69 | - declarations: [comp], | |
70 | - imports: moduleImports | |
71 | - }) | |
72 | - class DynamicComponentInstanceModule extends DynamicComponentModule {} | |
73 | - try { | |
74 | - this.compiler.compileModuleAsync(DynamicComponentInstanceModule).then( | |
75 | - (module) => { | |
76 | - const moduleRef = module.create(this.injector); | |
77 | - const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp); | |
78 | - this.dynamicComponentModulesMap.set(factory, { | |
79 | - moduleRef, | |
80 | - moduleType: module.moduleType | |
81 | - }); | |
82 | - dymamicComponentFactorySubject.next(factory); | |
83 | - dymamicComponentFactorySubject.complete(); | |
62 | + import('@angular/compiler').then( | |
63 | + () => { | |
64 | + const comp = this.createDynamicComponent(componentType, template); | |
65 | + let moduleImports: Type<any>[] = [CommonModule]; | |
66 | + if (modules) { | |
67 | + moduleImports = [...moduleImports, ...modules]; | |
84 | 68 | } |
85 | - ).catch( | |
86 | - (e) => { | |
69 | + // noinspection AngularInvalidImportedOrDeclaredSymbol | |
70 | + const dynamicComponentInstanceModule = NgModule({ | |
71 | + declarations: [comp], | |
72 | + imports: moduleImports | |
73 | + })(class DynamicComponentInstanceModule extends DynamicComponentModule {}); | |
74 | + try { | |
75 | + this.compiler.compileModuleAsync(dynamicComponentInstanceModule).then( | |
76 | + (module) => { | |
77 | + const moduleRef = module.create(this.injector); | |
78 | + const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp); | |
79 | + this.dynamicComponentModulesMap.set(factory, { | |
80 | + moduleRef, | |
81 | + moduleType: module.moduleType | |
82 | + }); | |
83 | + dymamicComponentFactorySubject.next(factory); | |
84 | + dymamicComponentFactorySubject.complete(); | |
85 | + } | |
86 | + ).catch( | |
87 | + (e) => { | |
88 | + dymamicComponentFactorySubject.error(e); | |
89 | + } | |
90 | + ); | |
91 | + } catch (e) { | |
87 | 92 | dymamicComponentFactorySubject.error(e); |
88 | 93 | } |
89 | - ); | |
90 | - } catch (e) { | |
91 | - dymamicComponentFactorySubject.error(e); | |
92 | - } | |
94 | + } | |
95 | + ); | |
93 | 96 | return dymamicComponentFactorySubject.asObservable(); |
94 | 97 | } |
95 | 98 | ... | ... |
... | ... | @@ -78,28 +78,31 @@ export class ResourcesService { |
78 | 78 | (module) => { |
79 | 79 | const modules = this.extractNgModules(module); |
80 | 80 | if (modules.length) { |
81 | - const tasks: Promise<ModuleWithComponentFactories<any>>[] = []; | |
82 | - for (const m of modules) { | |
83 | - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); | |
84 | - } | |
85 | - forkJoin(tasks).subscribe((compiled) => { | |
86 | - try { | |
87 | - const componentFactories: ComponentFactory<any>[] = []; | |
88 | - for (const c of compiled) { | |
89 | - c.ngModuleFactory.create(this.injector); | |
90 | - componentFactories.push(...c.componentFactories); | |
81 | + import('@angular/compiler').then( | |
82 | + () => { | |
83 | + const tasks: Promise<ModuleWithComponentFactories<any>>[] = []; | |
84 | + for (const m of modules) { | |
85 | + tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); | |
91 | 86 | } |
92 | - this.loadedFactories[url].next(componentFactories); | |
93 | - this.loadedFactories[url].complete(); | |
94 | - } catch (e) { | |
95 | - this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`)); | |
96 | - delete this.loadedFactories[url]; | |
97 | - } | |
98 | - }, | |
99 | - (e) => { | |
100 | - this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`)); | |
101 | - delete this.loadedFactories[url]; | |
102 | - }); | |
87 | + forkJoin(tasks).subscribe((compiled) => { | |
88 | + try { | |
89 | + const componentFactories: ComponentFactory<any>[] = []; | |
90 | + for (const c of compiled) { | |
91 | + c.ngModuleFactory.create(this.injector); | |
92 | + componentFactories.push(...c.componentFactories); | |
93 | + } | |
94 | + this.loadedFactories[url].next(componentFactories); | |
95 | + this.loadedFactories[url].complete(); | |
96 | + } catch (e) { | |
97 | + this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`)); | |
98 | + delete this.loadedFactories[url]; | |
99 | + } | |
100 | + }, | |
101 | + (e) => { | |
102 | + this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`)); | |
103 | + delete this.loadedFactories[url]; | |
104 | + }); } | |
105 | + ); | |
103 | 106 | } else { |
104 | 107 | this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`)); |
105 | 108 | delete this.loadedFactories[url]; |
... | ... | @@ -133,26 +136,30 @@ export class ResourcesService { |
133 | 136 | } catch (e) { |
134 | 137 | } |
135 | 138 | if (modules && modules.length) { |
136 | - const tasks: Promise<ModuleWithComponentFactories<any>>[] = []; | |
137 | - for (const m of modules) { | |
138 | - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); | |
139 | - } | |
140 | - forkJoin(tasks).subscribe((compiled) => { | |
141 | - try { | |
142 | - for (const c of compiled) { | |
143 | - c.ngModuleFactory.create(this.injector); | |
144 | - } | |
145 | - this.loadedModules[url].next(modules); | |
146 | - this.loadedModules[url].complete(); | |
147 | - } catch (e) { | |
148 | - this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`)); | |
149 | - delete this.loadedModules[url]; | |
139 | + import('@angular/compiler').then( | |
140 | + () => { | |
141 | + const tasks: Promise<ModuleWithComponentFactories<any>>[] = []; | |
142 | + for (const m of modules) { | |
143 | + tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); | |
150 | 144 | } |
151 | - }, | |
152 | - (e) => { | |
153 | - this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`)); | |
154 | - delete this.loadedModules[url]; | |
155 | - }); | |
145 | + forkJoin(tasks).subscribe((compiled) => { | |
146 | + try { | |
147 | + for (const c of compiled) { | |
148 | + c.ngModuleFactory.create(this.injector); | |
149 | + } | |
150 | + this.loadedModules[url].next(modules); | |
151 | + this.loadedModules[url].complete(); | |
152 | + } catch (e) { | |
153 | + this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`)); | |
154 | + delete this.loadedModules[url]; | |
155 | + } | |
156 | + }, | |
157 | + (e) => { | |
158 | + this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`)); | |
159 | + delete this.loadedModules[url]; | |
160 | + }); | |
161 | + } | |
162 | + ); | |
156 | 163 | } else { |
157 | 164 | this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`)); |
158 | 165 | delete this.loadedModules[url]; | ... | ... |
... | ... | @@ -77,7 +77,7 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On |
77 | 77 | this.complexFilterPredicate = predicate; |
78 | 78 | } |
79 | 79 | |
80 | - private openComplexFilterDialog() { | |
80 | + public openComplexFilterDialog() { | |
81 | 81 | this.dialog.open<ComplexFilterPredicateDialogComponent, ComplexFilterPredicateDialogData, |
82 | 82 | ComplexFilterPredicateInfo>(ComplexFilterPredicateDialogComponent, { |
83 | 83 | disableClose: true, | ... | ... |
... | ... | @@ -38,7 +38,7 @@ export interface AlarmRuleConditionDialogData { |
38 | 38 | selector: 'tb-alarm-rule-condition-dialog', |
39 | 39 | templateUrl: './alarm-rule-condition-dialog.component.html', |
40 | 40 | providers: [{provide: ErrorStateMatcher, useExisting: AlarmRuleConditionDialogComponent}], |
41 | - styleUrls: ['/alarm-rule-condition-dialog.component.scss'] | |
41 | + styleUrls: ['./alarm-rule-condition-dialog.component.scss'] | |
42 | 42 | }) |
43 | 43 | export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRuleConditionDialogComponent, AlarmCondition> |
44 | 44 | implements OnInit, ErrorStateMatcher { | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { MatDialogRef } from '@angular/material/dialog'; |
18 | -import { Inject, InjectionToken } from '@angular/core'; | |
18 | +import { Directive, Inject, InjectionToken } from '@angular/core'; | |
19 | 19 | import { Store } from '@ngrx/store'; |
20 | 20 | import { AppState } from '@core/core.state'; |
21 | 21 | import { Router } from '@angular/router'; |
... | ... | @@ -30,6 +30,7 @@ export interface CustomDialogData { |
30 | 30 | [key: string]: any; |
31 | 31 | } |
32 | 32 | |
33 | +@Directive() | |
33 | 34 | export class CustomDialogComponent extends PageComponent { |
34 | 35 | |
35 | 36 | [key: string]: any; | ... | ... |
... | ... | @@ -154,7 +154,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, |
154 | 154 | private alarmsTitlePattern: string; |
155 | 155 | |
156 | 156 | private displayDetails = true; |
157 | - private allowAcknowledgment = true; | |
157 | + public allowAcknowledgment = true; | |
158 | 158 | private allowClear = true; |
159 | 159 | |
160 | 160 | private defaultPageSize = 10; | ... | ... |
... | ... | @@ -186,11 +186,16 @@ export type TripAnimationSettings = { |
186 | 186 | usePointAsAnchor: boolean; |
187 | 187 | normalizationStep: number; |
188 | 188 | showPolygon: boolean; |
189 | + showLabel: boolean; | |
190 | + showTooltip: boolean; | |
189 | 191 | latKeyName: string; |
190 | 192 | lngKeyName: string; |
191 | 193 | rotationAngle: number; |
192 | 194 | label: string; |
193 | 195 | tooltipPattern: string; |
196 | + tooltipColor: string; | |
197 | + tooltipOpacity: number; | |
198 | + tooltipFontColor: string; | |
194 | 199 | useTooltipFunction: boolean; |
195 | 200 | useLabelFunction: boolean; |
196 | 201 | pointAsAnchorFunction: GenericFunction; | ... | ... |
... | ... | @@ -102,11 +102,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni |
102 | 102 | ctx: WidgetContext; |
103 | 103 | |
104 | 104 | private formResize$: ResizeObserver; |
105 | - private settings: MultipleInputWidgetSettings; | |
105 | + public settings: MultipleInputWidgetSettings; | |
106 | 106 | private widgetConfig: WidgetConfig; |
107 | 107 | private subscription: IWidgetSubscription; |
108 | 108 | private datasources: Array<Datasource>; |
109 | - private sources: Array<MultipleInputWidgetSource> = []; | |
109 | + public sources: Array<MultipleInputWidgetSource> = []; | |
110 | 110 | |
111 | 111 | isVerticalAlignment: boolean; |
112 | 112 | inputWidthSettings: string; | ... | ... |
... | ... | @@ -126,7 +126,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI |
126 | 126 | private defaultPageSize = 10; |
127 | 127 | private defaultSortOrder = '-0'; |
128 | 128 | private hideEmptyLines = false; |
129 | - private showTimestamp = true; | |
129 | + public showTimestamp = true; | |
130 | 130 | private dateFormatFilter: string; |
131 | 131 | |
132 | 132 | private searchAction: WidgetAction = { | ... | ... |
... | ... | @@ -36,12 +36,14 @@ import { JsonFormProps } from './react/json-form.models'; |
36 | 36 | import inspector from 'schema-inspector'; |
37 | 37 | import * as tinycolor_ from 'tinycolor2'; |
38 | 38 | import { DialogService } from '@app/core/services/dialog.service'; |
39 | -import * as React from 'react'; | |
40 | -import * as ReactDOM from 'react-dom'; | |
41 | -import ReactSchemaForm from './react/json-form-react'; | |
39 | +// import * as React from 'react'; | |
40 | +// import * as ReactDOM from 'react-dom'; | |
41 | +// import ReactSchemaForm from './react/json-form-react'; | |
42 | 42 | import JsonFormUtils from './react/json-form-utils'; |
43 | 43 | import { JsonFormComponentData } from './json-form-component.models'; |
44 | 44 | import { GroupInfo } from '@shared/models/widget.models'; |
45 | +import { Observable } from 'rxjs/internal/Observable'; | |
46 | +import { forkJoin, from } from 'rxjs'; | |
45 | 47 | |
46 | 48 | const tinycolor = tinycolor_; |
47 | 49 | |
... | ... | @@ -252,11 +254,35 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato |
252 | 254 | if (destroy) { |
253 | 255 | this.destroyReactSchemaForm(); |
254 | 256 | } |
255 | - ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement); | |
257 | + | |
258 | + // import ReactSchemaForm from './react/json-form-react'; | |
259 | + const reactSchemaFormObservables: Observable<any>[] = []; | |
260 | + reactSchemaFormObservables.push(from(import('react'))); | |
261 | + reactSchemaFormObservables.push(from(import('react-dom'))); | |
262 | + reactSchemaFormObservables.push(from(import('./react/json-form-react'))); | |
263 | + forkJoin(reactSchemaFormObservables).subscribe( | |
264 | + (modules) => { | |
265 | + const react = modules[0]; | |
266 | + const reactDom = modules[1]; | |
267 | + const jsonFormReact = modules[2].default; | |
268 | + reactDom.render(react.createElement(jsonFormReact, this.formProps), this.reactRootElmRef.nativeElement); | |
269 | + } | |
270 | + ); | |
271 | + /* import('./react/json-form-react').then( | |
272 | + (mod) => { | |
273 | + ReactDOM.render(React.createElement(mod.default, this.formProps), this.reactRootElmRef.nativeElement); | |
274 | + } | |
275 | + );*/ | |
276 | + // ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement); | |
256 | 277 | } |
257 | 278 | |
258 | 279 | private destroyReactSchemaForm() { |
259 | - ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement); | |
280 | + import('react-dom').then( | |
281 | + (reactDom) => { | |
282 | + reactDom.unmountComponentAtNode(this.reactRootElmRef.nativeElement); | |
283 | + } | |
284 | + ); | |
285 | + // ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement); | |
260 | 286 | } |
261 | 287 | |
262 | 288 | private validateModel(): boolean { | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; |
18 | 18 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
19 | -import Raphael, { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael'; | |
19 | +import { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael'; | |
20 | 20 | import * as tinycolor_ from 'tinycolor2'; |
21 | 21 | |
22 | 22 | const tinycolor = tinycolor_; |
... | ... | @@ -90,10 +90,14 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges { |
90 | 90 | if (this.paper) { |
91 | 91 | this.paper.remove(); |
92 | 92 | } |
93 | - this.paper = Raphael($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize); | |
94 | - const center = this.canvasSize / 2; | |
95 | - this.circleElement = this.paper.circle(center, center, this.radius); | |
96 | - this.draw(); | |
93 | + import('raphael').then( | |
94 | + (raphael) => { | |
95 | + this.paper = raphael.default($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize); | |
96 | + const center = this.canvasSize / 2; | |
97 | + this.circleElement = this.paper.circle(center, center, this.radius); | |
98 | + this.draw(); | |
99 | + } | |
100 | + ); | |
97 | 101 | } |
98 | 102 | |
99 | 103 | private draw() { | ... | ... |