From beb19a07ec591fc5b3944d4b67ad566567dc72e9 Mon Sep 17 00:00:00 2001 From: William Blythe Date: Wed, 24 Oct 2018 10:31:11 +1100 Subject: [PATCH] more work --- .babelrc | 27 ++++++++-- package.json | 14 ++++-- src/app/Coriolis.jsx | 50 ++++++++++--------- .../{stores/model/build.js => model/Build.js} | 11 ++-- src/app/model/index.js | 22 ++++++++ src/app/model/schema.js | 15 ++++++ src/app/pages/OutfittingPage.jsx | 29 ++++++----- src/app/stores/Persist.js | 49 +++++++++++------- src/app/stores/model/index.js | 8 --- src/app/stores/model/schema.js | 15 ------ src/{ => app}/sw.js | 0 webpack.config.dev.js | 13 ++++- webpack.config.prod.js | 4 +- 13 files changed, 164 insertions(+), 93 deletions(-) rename src/app/{stores/model/build.js => model/Build.js} (57%) create mode 100644 src/app/model/index.js create mode 100644 src/app/model/schema.js delete mode 100644 src/app/stores/model/index.js delete mode 100644 src/app/stores/model/schema.js rename src/{ => app}/sw.js (100%) diff --git a/.babelrc b/.babelrc index 5662b7a9..f4bb2eca 100644 --- a/.babelrc +++ b/.babelrc @@ -1,19 +1,29 @@ { "presets": [ - ["@babel/preset-env", {"modules": "commonjs"}], + [ + "@babel/preset-env", + { + "modules": "commonjs" + } + ], "@babel/preset-react" ], "plugins": [ - "@babel/plugin-syntax-dynamic-import", - "@babel/plugin-syntax-import-meta", - ["@babel/plugin-proposal-class-properties", { "loose": true }], - "@babel/plugin-proposal-json-strings", [ "@babel/plugin-proposal-decorators", { "legacy": true } ], + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-syntax-import-meta", + [ + "@babel/plugin-proposal-class-properties", + { + "loose": true + } + ], + "@babel/plugin-proposal-json-strings", "@babel/plugin-proposal-function-sent", "@babel/plugin-proposal-export-namespace-from", "@babel/plugin-proposal-numeric-separator", @@ -27,6 +37,13 @@ "proposal": "minimal" } ], + [ + "@babel/plugin-transform-runtime", + { + "helpers": true, + "regenerator": true + } + ], "@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-do-expressions", "@babel/plugin-proposal-function-bind" diff --git a/package.json b/package.json index f931a750..2d1aa7fd 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,8 @@ }, "devDependencies": { "@babel/core": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-decorators": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.1.0", + "@babel/plugin-proposal-decorators": "^7.1.2", "@babel/plugin-proposal-do-expressions": "^7.0.0", "@babel/plugin-proposal-export-default-from": "^7.0.0", "@babel/plugin-proposal-export-namespace-from": "^7.0.0", @@ -72,12 +72,12 @@ "@babel/plugin-proposal-throw-expressions": "^7.0.0", "@babel/plugin-syntax-dynamic-import": "^7.0.0", "@babel/plugin-syntax-import-meta": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.1.0", "@babel/preset-env": "^7.0.0", "@babel/preset-react": "^7.0.0", "appcache-webpack-plugin": "^1.4.0", - "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.0.1", - "babel-jest": "^23.6.0", + "babel-jest": "^23.4.2", "babel-loader": "^8.0.0", "copy-webpack-plugin": "^4.5.2", "create-react-class": "^15.6.3", @@ -118,11 +118,14 @@ "webpack-cli": "^3.1.1", "webpack-dev-server": "^3.1.9", "webpack-notifier": "^1.6.0", - "workbox-webpack-plugin": "^3.6.1" + "workbox-webpack-plugin": "^3.6.1", + "worker-loader": "^2.0.0" }, "dependencies": { "@babel/polyfill": "^7.0.0", + "@babel/runtime": "^7.1.2", "@nozbe/watermelondb": "^0.6.2", + "babel-runtime": "^6.26.0", "browserify-zlib-next": "^1.0.1", "classnames": "^2.2.6", "coriolis-data": "../coriolis-data", @@ -130,6 +133,7 @@ "detect-browser": "^3.0.1", "fbemitter": "^2.1.1", "lodash": "^4.17.11", + "lokijs": "^1.5.5", "lz-string": "^1.4.4", "pako": "^1.0.6", "prop-types": "^15.6.2", diff --git a/src/app/Coriolis.jsx b/src/app/Coriolis.jsx index 3f1f7159..ce1894dd 100644 --- a/src/app/Coriolis.jsx +++ b/src/app/Coriolis.jsx @@ -349,30 +349,32 @@ export default class Coriolis extends React.Component { // *Don't* register service worker file in, e.g., a scripts/ sub-directory! // See https://github.com/slightlyoff/ServiceWorker/issues/468 const self = this; - register('/service-worker.js', { - ready (registration) { - console.log('Service worker is active.') - }, - registered (registration) { - console.log('Service worker has been registered.') - }, - cached (registration) { - console.log('Content has been cached for offline use.') - }, - updatefound (registration) { - console.log('New content is downloading.') - }, - updated (registration) { - self.setState({ appCacheUpdate: true }); - console.log('New content is available; please refresh.') - }, - offline () { - console.log('No internet connection found. App is running in offline mode.') - }, - error (error) { - console.error('Error during service worker registration:', error) - } - }); + if (process.env.NODE_ENV === 'production') { + register('/service-worker.js', { + ready (registration) { + console.log('Service worker is active.') + }, + registered (registration) { + console.log('Service worker has been registered.') + }, + cached (registration) { + console.log('Content has been cached for offline use.') + }, + updatefound (registration) { + console.log('New content is downloading.') + }, + updated (registration) { + self.setState({ appCacheUpdate: true }); + console.log('New content is available; please refresh.') + }, + offline () { + console.log('No internet connection found. App is running in offline mode.') + }, + error (error) { + console.error('Error during service worker registration:', error) + } + }); + } } window.onerror = this._onError.bind(this); window.addEventListener('resize', () => this.emitter.emit('windowResize')); diff --git a/src/app/stores/model/build.js b/src/app/model/Build.js similarity index 57% rename from src/app/stores/model/build.js rename to src/app/model/Build.js index 21f83e97..6b0a7a3d 100644 --- a/src/app/stores/model/build.js +++ b/src/app/model/Build.js @@ -2,8 +2,11 @@ import { Model } from '@nozbe/watermelondb'; import { field } from '@nozbe/watermelondb/decorators'; export default class Build extends Model { - static table = 'builds' - @field('title') title - @field('code') code - @field('shipId') shipId + static table = 'builds'; + + @field('title') title; + + @field('code') code; + + @field('ship_id') ship_id; } diff --git a/src/app/model/index.js b/src/app/model/index.js new file mode 100644 index 00000000..3c120105 --- /dev/null +++ b/src/app/model/index.js @@ -0,0 +1,22 @@ +import { Database } from '@nozbe/watermelondb'; +import LokiJSAdapter from '@nozbe/watermelondb/adapters/lokijs'; + +import { coriolisSchema } from './schema'; +import Build from './Build'; +// import Post from './model/Post' // ⬅️ You'll import your Models here + +// First, create the adapter to the underlying database: +const adapter = new LokiJSAdapter({ + dbName: 'coriolis', // ⬅️ Give your database a name! + schema: coriolisSchema +}); + +// Then, make a Watermelon database from it! +export const database = new Database({ + adapter, + modelClasses: [ + Build + ] +}); + +export const buildsCollection = database.collections.get('builds'); diff --git a/src/app/model/schema.js b/src/app/model/schema.js new file mode 100644 index 00000000..53e0c97f --- /dev/null +++ b/src/app/model/schema.js @@ -0,0 +1,15 @@ +import {appSchema, tableSchema} from '@nozbe/watermelondb'; + +export const coriolisSchema = appSchema({ + version: 2, + tables: [ + tableSchema({ + name: 'builds', + columns: [ + {name: 'title', type: 'string', isIndexed: true}, + {name: 'code', type: 'string'}, + {name: 'ship_id', type: 'string'} + ] + }) + ] +}); diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx index ae042559..4ff80477 100644 --- a/src/app/pages/OutfittingPage.jsx +++ b/src/app/pages/OutfittingPage.jsx @@ -38,6 +38,7 @@ import ModalExport from '../components/ModalExport'; import ModalPermalink from '../components/ModalPermalink'; import ModalShoppingList from '../components/ModalShoppingList'; import ModalOrbis from '../components/ModalOrbis'; +import { autoBind } from 'react-extras'; /** * Document Title Generator @@ -62,14 +63,7 @@ export default class OutfittingPage extends Page { super(props, context); // window.Perf = Perf; this.state = this._initState(props, context); - this._keyDown = this._keyDown.bind(this); - this._exportBuild = this._exportBuild.bind(this); - this._pipsUpdated = this._pipsUpdated.bind(this); - this._boostUpdated = this._boostUpdated.bind(this); - this._cargoUpdated = this._cargoUpdated.bind(this); - this._fuelUpdated = this._fuelUpdated.bind(this); - this._opponentUpdated = this._opponentUpdated.bind(this); - this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this); + autoBind(this); this._sectionMenuRefs = {}; } @@ -83,9 +77,9 @@ export default class OutfittingPage extends Page { let params = context.route.params; let shipId = params.ship; let code = params.code; + let savedCode = code; let buildName = params.bn; let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults - let savedCode = Persist.getBuild(shipId, buildName); if (!data) { return { error: { message: 'Ship not found: ' + shipId } }; } @@ -144,6 +138,13 @@ export default class OutfittingPage extends Page { }; } + async getBuild() { + const build = await Persist.getBuild(this.state.shipId, this.state.buildName); + if (build) { + this.setState({id: build.id, savedCode: build.code}) + } + } + /** * Handle build name change and update state * @param {SyntheticEvent} event React Event @@ -417,13 +418,14 @@ export default class OutfittingPage extends Page { /** * Save the current build */ - _saveBuild() { - const { ship, buildName, newBuildName, shipId } = this.state; - + async _saveBuild() { + const { ship, buildName, newBuildName, shipId, id } = this.state; + this.getBuild(); // If this is a stock ship the code won't be set, so ensure that we have it const code = this.state.code || ship.toString(); - Persist.saveBuild(shipId, newBuildName, code); + const yes = await Persist.saveBuild(id, newBuildName, code, shipId); + console.log(yes) this._updateRoute(shipId, newBuildName, code); let opponent, opponentBuild, opponentSys, opponentEng, opponentWep; @@ -660,6 +662,7 @@ export default class OutfittingPage extends Page { * Add listeners when about to mount */ componentWillMount() { + this.getBuild() document.addEventListener('keydown', this._keyDown); } diff --git a/src/app/stores/Persist.js b/src/app/stores/Persist.js index d0be7ef9..33f41e2f 100644 --- a/src/app/stores/Persist.js +++ b/src/app/stores/Persist.js @@ -1,5 +1,7 @@ import { EventEmitter } from 'fbemitter'; import { Insurance } from '../shipyard/Constants'; +import { buildsCollection, database } from '../model'; +import {Q} from '@nozbe/watermelondb'; const LS_KEY_BUILDS = 'builds'; const LS_KEY_COMPARISONS = 'comparisons'; @@ -81,7 +83,7 @@ export class Persist extends EventEmitter { localStorage.setItem('test', 'test'); localStorage.removeItem('test'); LS = localStorage; - } catch(e) { + } catch (e) { LS = null; } @@ -105,7 +107,7 @@ export class Persist extends EventEmitter { this.comparisons = comparisonJson && typeof comparisonJson == 'object' ? comparisonJson : {}; this.costTab = _getString(LS_KEY_COST_TAB); this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB); - this.state = _get(LS_KEY_STATE); + this.state = _get(LS_KEY_STATE); this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1; this.matsPerGrade = matsPerGrade || { 1: 2, @@ -132,7 +134,7 @@ export class Persist extends EventEmitter { let newValue = e.newValue; try { - switch(e.key) { + switch (e.key) { case LS_KEY_BUILDS: this.builds = newValue ? JSON.parse(newValue) : {}; this.emit('builds'); @@ -249,17 +251,27 @@ export class Persist extends EventEmitter { /** * Persist a ship build in local storage. * - * @param {String} shipId The unique id for a model of ship + * @param {String} id The unique id for the ship or '' + * @param {String} shipId The coriolis ship id * @param {String} name The name of the build * @param {String} code The serialized code */ - saveBuild(shipId, name, code) { - if (!this.builds[shipId]) { - this.builds[shipId] = {}; + async saveBuild(id, name, code, shipId) { + if (id) { + const build = await buildsCollection.find(id); + if (build) { + return build.update(newBuild => { + newBuild.title = name; + newBuild.body = code; + }); + } } - this.builds[shipId][name] = code; - _put(LS_KEY_BUILDS, this.builds); - this.emit('builds'); + + return await buildsCollection.create(build => { + build.title = name; + build.ship_id = shipId; + build.body = code; + }); } /** @@ -270,11 +282,10 @@ export class Persist extends EventEmitter { * @param {String} name The name of the build * @return {String} The serialized build string. */ - getBuild(shipId, name) { - if (this.builds[shipId] && this.builds[shipId][name]) { - return this.builds[shipId][name]; - } - return null; + async getBuild(shipId, name) { + const build = await buildsCollection.query(Q.where('ship_id', shipId), Q.where('title', name)).fetch(); + console.log(build); + return build; } /** @@ -283,7 +294,7 @@ export class Persist extends EventEmitter { * @return {Object | Array} Object if Ship Id is not provided */ getBuilds(shipId) { - if(shipId && shipId.length > 0) { + if (shipId && shipId.length > 0) { return this.builds[shipId]; } return this.builds; @@ -362,7 +373,9 @@ export class Persist extends EventEmitter { } this.comparisons[name] = { facets, - builds: builds.map(b => { return { shipId: b.id || b.shipId, buildName: b.buildName }; }) + builds: builds.map(b => { + return { shipId: b.id || b.shipId, buildName: b.buildName }; + }) }; _put(LS_KEY_COMPARISONS, this.comparisons); this.emit('comparisons'); @@ -505,6 +518,7 @@ export class Persist extends EventEmitter { _put(LS_KEY_ROLLS, this.matsPerGrade); this.emit('matsPerGrade'); } + /** * Get the saved Mats per grade * @return {Object} # of rolls per grade @@ -556,6 +570,7 @@ export class Persist extends EventEmitter { this.outfittingTab = tabName; _put(LS_KEY_OUTFITTING_TAB, tabName); } + /** * Get the current outfitting tab * @return {string} the current outfitting tab diff --git a/src/app/stores/model/index.js b/src/app/stores/model/index.js deleted file mode 100644 index cecf20f9..00000000 --- a/src/app/stores/model/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import { appSchema, tableSchema } from '@nozbe/watermelondb'; - -export const buildSchema = appSchema({ - version: 1, - tables: [ - // tableSchemas go here... - ] -}); diff --git a/src/app/stores/model/schema.js b/src/app/stores/model/schema.js deleted file mode 100644 index 922f1f23..00000000 --- a/src/app/stores/model/schema.js +++ /dev/null @@ -1,15 +0,0 @@ -import { appSchema, tableSchema } from '@nozbe/watermelondb'; - -export const mySchema = appSchema({ - version: 2, - tables: [ - tableSchema({ - name: 'builds', - columns: [ - { name: 'title', type: 'string' }, - { name: 'code', type: 'string' }, - { name: 'shipId', type: 'string' } - ] - }) - ] -}); diff --git a/src/sw.js b/src/app/sw.js similarity index 100% rename from src/sw.js rename to src/app/sw.js diff --git a/webpack.config.dev.js b/webpack.config.dev.js index bd0625ae..5990847b 100644 --- a/webpack.config.dev.js +++ b/webpack.config.dev.js @@ -29,8 +29,15 @@ module.exports = { output: { path: path.join(__dirname, 'build'), filename: 'app.js', + globalObject: 'this', publicPath: '/' }, + node: { + fs: 'empty', + net: 'empty', + tls: 'empty', + 'crypto': 'empty' + }, plugins: [ new CopyWebpackPlugin(['src/.htaccess']), // new webpack.optimize.CommonsChunkPlugin({ @@ -56,8 +63,12 @@ module.exports = { module: { rules: [ { test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) }, - { test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) }, + { + test: /\.less$/, + loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) + }, { test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') }, + { test: /\.worker\.js$/, use: { loader: 'worker-loader' } }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' }, diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 5ad9d7e7..a535d8f6 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -22,6 +22,7 @@ module.exports = { publicPath: '/', globalObject: 'this' }, + node: { fs: 'empty' }, mode: 'production', optimization: { minimize: true, @@ -53,13 +54,14 @@ module.exports = { // appVersion: `${pkgJson.version}-${buildDate.toISOString()}` // }), new InjectManifest({ - swSrc: './src/sw.js', + swSrc: './src/app/sw.js', importWorkboxFrom: 'cdn', swDest: 'service-worker.js' }), ], module: { rules: [ + {test: /\.worker\.js$/, use: {loader: 'worker-loader'}}, { test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) }, { test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) }, { test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },