Compare commits

...

2 Commits

Author SHA1 Message Date
William Blythe
beb19a07ec more work 2018-10-24 10:31:11 +11:00
William Blythe
98113e8807 initial watermelondb 2018-10-24 09:10:53 +11:00
11 changed files with 170 additions and 66 deletions

View File

@@ -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"

View File

@@ -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,10 +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",
@@ -129,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",

View File

@@ -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'));

12
src/app/model/Build.js Normal file
View File

@@ -0,0 +1,12 @@
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('ship_id') ship_id;
}

22
src/app/model/index.js Normal file
View File

@@ -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');

15
src/app/model/schema.js Normal file
View File

@@ -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'}
]
})
]
});

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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' },

View File

@@ -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') },