diff --git a/app/views/layouts/apified.html.erb b/app/views/layouts/apified.html.erb index bdaa7a87..43dfd4d8 100644 --- a/app/views/layouts/apified.html.erb +++ b/app/views/layouts/apified.html.erb @@ -12,16 +12,19 @@ window._csrf = "<%= form_authenticity_token %>" - <%= IncludeAsset.js 'app/react.js' %> - <%= IncludeAsset.js 'app/react-dom.js' %> - <%= IncludeAsset.js 'app/vendor.js' %> - <%= yield :javascripts %> + <%= render 'layouts/stylesheets' %> <%= IncludeAsset.css 'client/css/global/page.css' %> <%= IncludeAsset.css 'client/css/bootstrap.css' %> +<%= IncludeAsset.js 'app/loading_indicator.js' %> +<%= IncludeAsset.js 'app/react.js' %> +<%= IncludeAsset.js 'app/react-dom.js' %> +<%= IncludeAsset.js 'app/vendor.js' %> +<%= yield :javascripts %> <%= yield %> + \ No newline at end of file diff --git a/javascripts/app/loading_indicator.ts b/javascripts/app/loading_indicator.ts new file mode 100644 index 00000000..56e74f4b --- /dev/null +++ b/javascripts/app/loading_indicator.ts @@ -0,0 +1,52 @@ +// License: LGPL-3.0-or-later + + +class PageProgressBar { + template: HTMLTemplateElement + + constructor() { + this.template = document.createElement('template') + this.template.innerHTML = `
` + } + + beginPageLoad(): void { + let element = this.getElem() + if (!element) { + let firstChild = document.body.firstChild + let clone = document.importNode(this.template.content, true) + if (firstChild) { + + document.body.insertBefore(clone, firstChild) + } + else { + document.body.appendChild(clone) + } + } + + return + } + + finishPageLoad() { + let element = this.getElem() + if (element) { + element.remove() + } + } + + private getElem(): Element { + return document.getElementById("pageProgressBar") + } +} + +const pp: PageProgressBar = new PageProgressBar(); + +(window as any).pageProgress = pp + +pp.beginPageLoad() + + + + + + + diff --git a/javascripts/src/components/common/Root.tsx b/javascripts/src/components/common/Root.tsx index ce86373f..1118351b 100644 --- a/javascripts/src/components/common/Root.tsx +++ b/javascripts/src/components/common/Root.tsx @@ -25,6 +25,13 @@ export default class Root extends React.Component { apiManager: ApiManager + componentDidMount(){ + let pageProgress = (window as any).pageProgress + if (pageProgress && pageProgress.finishPageLoad){ + pageProgress.finishPageLoad() + } + + } render() { if (!this.apiManager){ this.apiManager = new ApiManager(APIS.concat(CustomAPIS.APIS as Array), CSRFInterceptor) diff --git a/package-lock.json b/package-lock.json index 99e4bfa9..f4d4ce53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -347,7 +347,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, "requires": { "kind-of": "3.2.2", "longest": "1.0.1", @@ -358,7 +357,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -2484,7 +2482,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, "requires": { "align-text": "0.1.4", "lazy-cache": "1.0.4" @@ -7028,8 +7025,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-builtin-module": { "version": "1.0.0", @@ -9553,8 +9549,7 @@ "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" }, "lazystream": { "version": "0.1.0", @@ -10204,8 +10199,7 @@ "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, "loose-envify": { "version": "1.3.1", @@ -13642,8 +13636,7 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "repeating": { "version": "2.0.1", @@ -13830,7 +13823,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, "requires": { "align-text": "0.1.4" } @@ -14690,8 +14682,7 @@ "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" }, "source-map": { "version": "0.5.7", @@ -16517,7 +16508,6 @@ "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, "requires": { "source-map": "0.5.7", "uglify-to-browserify": "1.0.2", @@ -16527,14 +16517,12 @@ "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, "requires": { "center-align": "0.1.3", "right-align": "0.1.3", @@ -16544,14 +16532,12 @@ "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, "requires": { "camelcase": "1.2.1", "cliui": "2.1.0", @@ -16565,14 +16551,12 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, "optional": true }, "uglifyjs-webpack-plugin": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, "requires": { "source-map": "0.5.7", "uglify-js": "2.8.29", @@ -17693,7 +17677,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", - "dev": true, "requires": { "source-list-map": "2.0.0", "source-map": "0.6.1" @@ -17702,8 +17685,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -17779,8 +17761,7 @@ "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" }, "wordwrap": { "version": "0.0.3", diff --git a/package.json b/package.json index 162abe5f..b961bb20 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,13 @@ "watch": "npm run export-button-config && npm run export-i18n && npm run generate-api-js && npx webpack --watch", "build": "npm run export-button-config && npm run export-i18n && npm run generate-api-js && NODE_ENV=production npx webpack -p", "build-all": "script/compile-assets.sh && npm run build", - "test": "zuul --phantom --ui mocha-qunit -- spec/js/index.js", + "test": "rake spec && npm run build && npx jest", "test-browser": "zuul --local --ui mocha-qunit spec/js/index.js", "export-button-config": "bundle exec rake settings:generate_json", "export-i18n": "bundle exec rake settings:combine_translations", "generate-openapi": "rake oapi:gen", "get-codegen-cli": "script/download_codegen_cli.sh", - "generate-api-js": "npm run get-codegen-cli && npm run generate-openapi && java -jar .bin/swagger-codegen-cli.jar generate -l 'typescript-jquery' -i tmp/openapi.json -o javascripts/api -t lib/swagger-typescript-jquery", - "test": "rake spec && npm run build && npx jest" - + "generate-api-js": "npm run get-codegen-cli && npm run generate-openapi && java -jar .bin/swagger-codegen-cli.jar generate -l 'typescript-jquery' -i tmp/openapi.json -o javascripts/api -t lib/swagger-typescript-jquery" }, "devDependencies": { "@types/enzyme": "^3.1.9", @@ -141,6 +139,7 @@ "snabbdom": "0.3.0", "snake-case": "2.1.0", "superagent": "1.1.0", + "uglifyjs-webpack-plugin": "^0.4.6", "uuid": "2.0.2", "vdom-thunk": "3.0.0", "view-script": "0.3.6", diff --git a/webpack.config.js b/webpack.config.js index e8ef67d7..fd3e5c2a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,6 +8,7 @@ const webpack = require("webpack"); const CopyWebpackPlugin = require('copy-webpack-plugin') const merge = require('webpack-merge'); const CompressionPlugin = require("compression-webpack-plugin"); +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const config_button=require('./config/settings.json'); @@ -17,6 +18,7 @@ const translationPath = path.join(__dirname, 'public/javascripts/_final.js') const reactEntrySourcePath = path.join(__dirname, 'javascripts') const reactEntryOutputPath = path.join(__dirname, 'public') + let inProduction = process.env.NODE_ENV === 'production' let devToProdLibraries = { @@ -136,6 +138,17 @@ let targets = { new ExtractTextPlugin('bootstrap.css') ] }, + loading_indicator: { + module:{ + rules: common_rules + }, + entry: path.resolve(reactEntrySourcePath, "app", "loading_indicator.ts"), + output: { + path: path.resolve(reactEntryOutputPath, 'app'), + filename: 'loading_indicator.js' + }, + + }, react: { module:{ rules: common_rules @@ -172,6 +185,7 @@ let mergeToTargets = { if (inProduction) mergeToTargets = { plugins: [ + new UglifyJsPlugin(), new CompressionPlugin({ asset: '[path].gz' })