diff --git a/.babelrc b/.babelrc index 562a5e3..64fe89c 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,4 @@ { - "presets": ["es2015", "react", "stage-1"] + "presets": ["es2015", "react", "stage-1"], + "plugins": ["transform-decorators-legacy"] } diff --git a/chaotic-portal.sublime-project b/chaotic-portal.sublime-project index bc56dfd..6c34aae 100644 --- a/chaotic-portal.sublime-project +++ b/chaotic-portal.sublime-project @@ -1,10 +1,11 @@ { "folders": - [ - { - "path": "." - } - ], + [ + { + "path": ".", + "folder_exclude_patterns": ["build", "node_modules"] + } + ], "syntax_override": { "\\.js$": diff --git a/package.json b/package.json index e9d27a5..177aea5 100644 --- a/package.json +++ b/package.json @@ -13,24 +13,31 @@ "author": "Danude Sandstorm", "license": "MIT", "dependencies": { - "react": "^15.4.1", - "react-dom": "^15.4.1", - "react-interactive": "^0.5.1", - "react-router": "^3.0.0", + "lokijs": "^1.5.1", + "prop-types": "^15.6.0", + "mobx": "^3.3.1", + "mobx-react": "^4.3.3", + "react": "^16.0.0", + "react-dom": "^16.0.0", + "react-interactive": "^0.8.1", + "react-router": "^3.2.0", + "react-router-dom": "^4.2.2", + "rxjs": "^5.5.0", "whatwg-fetch": "^2.0.3" }, "devDependencies": { "babel-core": "^6.21.0", - "babel-eslint": "^7.1.1", + "babel-eslint": "^8.0.0", "babel-loader": "^6.2.10", + "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-preset-es2015": "^6.18.0", "babel-preset-react": "^6.16.0", "babel-preset-stage-1": "^6.16.0", - "eslint": "^3.12.2", - "eslint-config-airbnb": "^13.0.0", + "eslint": "^4.0.0", + "eslint-config-airbnb": "^16.0.0", "eslint-plugin-import": "^2.2.0", - "eslint-plugin-jsx-a11y": "^2.2.3", - "eslint-plugin-react": "^6.8.0", + "eslint-plugin-jsx-a11y": "^6.0.0", + "eslint-plugin-react": "^7.0.0", "webpack": "^1.14.0", "webpack-dev-server": "^1.16.2" } diff --git a/src/Base.js b/src/Base.js index 4bafd28..2e06e36 100644 --- a/src/Base.js +++ b/src/Base.js @@ -1,4 +1,5 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import Interactive from 'react-interactive'; import { Link } from 'react-router'; import s from './styles/app.style'; diff --git a/src/components/AppHelper.js b/src/components/AppHelper.js deleted file mode 100644 index 28e07c4..0000000 --- a/src/components/AppHelper.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; - -export const language = "ENG"; -export const bkgrnd = "05"; - -export function ChangePage(asParams) { - let location = "#"; - switch (asParams) { - case 'collect': - location = '/collection/'; - break; - case 'register': - break; - case 'build': - location = 'http://www.tradecardsonline.com/?action=selectCard&goal=DK&game_id=82'; - break; - case 'centerOval': - location = '/'; - break; - case 'enterTheCode': - break; - case 'trade': - location = 'http://www.tradecardsonline.com/?action=selectCard&goal=&game_id=82'; - break; - case 'portal': - location = '/portal/'; - break; - case 'forum': - location = 'http://chaoticbackup.forumotion.com'; - break; - case 'playNow': - location = 'http://www.tradecardsonline.com/?action=selectCard&goal=DK&game_id=82'; - break; - default: - location = '/construction/'; - break; - } - return location; -} diff --git a/src/components/CollectionDB.js b/src/components/CollectionDB.js new file mode 100644 index 0000000..8ab24be --- /dev/null +++ b/src/components/CollectionDB.js @@ -0,0 +1,86 @@ +import loki from 'lokijs'; +import {observable, autorun} from "mobx"; + +export default class CollectionDB { + @observable built = []; // Keeps track of what collections have been populated + + constructor(API) { + this.api = API; + // ignoring persistence for now + // this.setupDB(); + //autorun(() => console.log(this.creatures)); + let db = new loki("chaotic_portal.db"); + this.attacks = db.addCollection('attacks'); + this.battlegear = db.addCollection('battlegear'); + this.creatures = db.addCollection('creatures'); + this.locations = db.addCollection('locations'); + this.mugic = db.addCollection('mugic'); + this.db = db; + } + + // setupDB() { + // var self = this; + // let db = new loki("chaotic_portal.db", { autosave: true, autoload: true, autoloadCallback: databaseInitialize, autosaveInterval: 4000, persistenceMethod: 'localStorage' }); + // this.db = db; + + // let databaseInitialize = () => { + // var entries; + // if ((entries = db.getCollection("attacks")) === null) + // entries = db.addCollection("attacks"); + // self.attacks = entries; + + // if ((entries = db.getCollection("battlegear")) === null) + // entries = db.addCollection("battlegear"); + // self.battlegear = entries; + + // console.log(db.getCollection("creatures")); + // if ((entries = db.getCollection("creatures")) === null) + // entries = db.addCollection("creatures"); + // self.creatures = db.addCollection('creatures'); + + // if ((entries = db.getCollection("locations")) === null) + // entries = db.addCollection("locations"); + // self.locations = entries + + // if ((entries = db.getCollection("mugic")) === null) + // entries = db.addCollection("mugic"); + // self.mugic = entries; + // }; + // } + + setup(spreadsheet, callback) { + this.api.getSpreadsheet(spreadsheet, (data) => { + callback(data.map((item) => { + let temp = {}; + delete item.content; + for (const key of Object.keys(item)) { + temp[key] = item[key].$t; + } + return temp; + })); + }); + } + + setupAttacks() { + + } + + setupBattleGear() { + + } + + setupCreatures(tribe="Generic") { + this.setup(this.api.urls.Creatures[tribe], (data) => { + this.creatures.insert(data); + this.built.push("creatures_"+tribe); + }); + } + + setupLocations() { + + } + + setupMugic(tribe) { + + } +} diff --git a/src/components/ExampleComponent.js b/src/components/ExampleComponent.js index f08ddda..042119a 100644 --- a/src/components/ExampleComponent.js +++ b/src/components/ExampleComponent.js @@ -1,4 +1,5 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import Interactive from 'react-interactive'; import { Link } from 'react-router'; import s from '../styles/exampleComponent.style'; diff --git a/src/components/ExampleTwoDeepComponent.js b/src/components/ExampleTwoDeepComponent.js index 0646d5c..379cf17 100644 --- a/src/components/ExampleTwoDeepComponent.js +++ b/src/components/ExampleTwoDeepComponent.js @@ -1,4 +1,5 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import Interactive from 'react-interactive'; import { Link } from 'react-router'; import s from '../styles/exampleTwoDeepComponent.style'; diff --git a/src/components/PageNotFound.js b/src/components/PageNotFound.js index 8a4d3a2..5d13f85 100644 --- a/src/components/PageNotFound.js +++ b/src/components/PageNotFound.js @@ -1,4 +1,5 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import s from '../styles/pageNotFound.style'; const propTypes = { diff --git a/src/components/SpreadsheetData.js b/src/components/SpreadsheetData.js index 0fa569d..eebf412 100644 --- a/src/components/SpreadsheetData.js +++ b/src/components/SpreadsheetData.js @@ -1,39 +1,47 @@ -import React from 'react'; import 'whatwg-fetch'; +import CollectionDB from './CollectionDB'; +import {observable} from "mobx"; + +class API { + @observable portal = null; + @observable cards = null; + @observable urls = null; + instance = null; -class URLS { static base_url = "https://spreadsheets.google.com/feeds/list/"; static data_format = "/od6/public/values?alt=json"; // + "/od6/public/basic?alt=json"; // Alternate data format static base_spreadsheet = "1cUNmwV693zl2zqbH_IG4Wz8o9Va_sOHe7pAZF6M59Es"; get base_image() { return "https://drive.google.com/uc?id="; } + // Singleton + static getInstance() { + if (!this.instance) { this.instance = new API(); } + return this.instance; + } + static path(spreadsheetID) { - return URLS.base_url + spreadsheetID + URLS.data_format; + return API.base_url + spreadsheetID + API.data_format; } constructor() { - // This sets up urls - this.urls = {}; - var self = this; - this.getSpreadsheet(URLS.path(URLS.base_spreadsheet), function(data) { - data.forEach(function(d) { - if (!self.urls[d.gsx$type.$t]) self.urls[d.gsx$type.$t] = {}; - self.urls[d.gsx$type.$t][d.gsx$subtype.$t] = d.gsx$url.$t; + // This sets up urls and kicks off db + let urls = {}; + this.getSpreadsheet(API.path(API.base_spreadsheet), (data) => { + if (data == null) return; + data.forEach((d) => { + if (!urls[d.gsx$type.$t]) urls[d.gsx$type.$t] = {}; + urls[d.gsx$type.$t][d.gsx$subtype.$t] = API.path(d.gsx$url.$t); }); + this.urls = urls; + this.setupDB(); }); } - // Singleton - instance = null; - static getInstance() { - if (!URLS.instance) { URLS.instance = new URLS(); } - return URLS.instance; - } - getSpreadsheet(spreadsheet, callback) { fetch(spreadsheet) .then(function(response) { + // console.log(response); return response.json(); }).then(function(json) { return callback(json.feed.entry); @@ -43,31 +51,17 @@ class URLS { }); } - /* Creatures */ - /* TODO remove legacy getters when introducing state management*/ - get Creatures_Card_Data() { - return URLS.path("1fUFYhG1NLLkSTzrdbevm6ZMKNP6xLiKUZvM1sY10pVI"); + setupDB() { + try { + this.portal = new CollectionDB(this, "portal"); + this.cards = new CollectionDB(this, "cards"); + } + catch (err) { + console.log('setting up database failed', err); + } } - - get Creatures() { - return { - 'Overworld': URLS.path("1Z4_MmlV7uE34nLzrcxslqQKRwL4OBXNA15s7G8eteXU"), - 'Underworld': URLS.path("1c_XAxQsDIWVdzUxWl5t-K_mQumNjX-yrg0X-0NsZVts"), - 'Mipedian': URLS.path("1P4FKASfnhR46j2bqm89T9xhI1Yyyy-jiZ1CkRglSy2k"), - 'Danian': URLS.path("1-Lz-itwOobEvqr8HSLFFwg3JFkT64NbwyGFEPfj9rxU") - }; - } - - get Mugic() { - return { - 'Overworld': URLS.path("1KsVX5SkygwPP6I8yd6xgcN8gB746o_FTh6SK1TvAcbU"), - 'Underworld': URLS.path("1F7FHlob52cb_7J65cddM3The7w-kFiStBRd5wm4JRlA"), - 'Mipedian': URLS.path("1QDsiSUuBV_4Jn6mvl96EGU06XEoVqILGN4suYKvh9CA"), - 'Danian': URLS.path("1tEuwPGixJH2A03YtYL6Ar-MSFvtfrlaveT98GwJhw1g"), - 'Generic': URLS.path("1M9iAbpYAHq_ppwm0PZKUvZGfu-zbrK1CZMY16m4Plf8") - }; - } - } -export default URLS.getInstance(); +export default API.getInstance(); + +// export default new API(); diff --git a/src/components/UnderConstruction.js b/src/components/UnderConstruction.js index f774de7..e154f1f 100644 --- a/src/components/UnderConstruction.js +++ b/src/components/UnderConstruction.js @@ -1,4 +1,5 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import s from '../styles/pageNotFound.style'; const propTypes = { diff --git a/src/components/portal/Category/Creatures.js b/src/components/portal/Category/Creatures.js index f51e104..b7ea23e 100644 --- a/src/components/portal/Category/Creatures.js +++ b/src/components/portal/Category/Creatures.js @@ -4,54 +4,20 @@ import { Link } from 'react-router'; import PageNotFound from '../../PageNotFound'; import API from '../../SpreadsheetData'; import s from '../../../styles/app.style'; +import UnderConstruction from '../../UnderConstruction'; export default class Creatures extends React.Component { - constructor(props) { - super (props); - this.state = {tribe: '', creatures: {}}; - } - - componentWillReceiveProps(nextProps) { - this.getData(nextProps); - } - - componentDidMount() { - this.getData(this.props); - } - - // ** Process the tribe ** // - // /portal/Creatures/ - // /portal/{Tribe}/Creatures/ - // The first / gets counted - getData(props) { - if (props.children) return this.props = props; - let path = props.location.pathname.split("/"); - if (path[path.length-1] == "") path.pop(); // Remove trailing backslash - - // Set tribe - let tribe = (path.length === 4) ? path[2] : "All"; - this.setState({tribe: tribe}); - - // For each tribe, get its spreadsheet, set the state - var self = this; - let urls = (tribe == "All") ? API.Creatures : {[tribe]: API.Creatures[tribe]}; - Object.keys(urls).map((tribe) => { - API.getSpreadsheet(urls[tribe], (data) => { - self.setState({creatures: - Object.assign(self.state.creatures, {[tribe]: data}) - }); - }); - }); - // self.setState({creatures: this.state.creatures.concat([data])}); - } - - hacks(event) { - console.log(event); - this.setState({click: true}); - } - render() { + if (this.props.children) { + return (
{this.props.children}
); + } + return ( + + ); + } + + fakerender() { if (this.props.children) { return (
{this.props.children}
); } diff --git a/src/components/portal/Category/Mugic.js b/src/components/portal/Category/Mugic.js index e043748..3585e9b 100644 --- a/src/components/portal/Category/Mugic.js +++ b/src/components/portal/Category/Mugic.js @@ -8,25 +8,16 @@ import s from '../../../styles/app.style'; export default class Mugic extends React.Component { - constructor(props) { - super (props); - this.tribe = ''; - this.state = {mugic: {}}; - } - - componentDidMount() { - if (this.props.children) return; - var self = this; - let urls = (this.tribe == "All") ? API.Mugic : {[this.tribe]: API.Mugic[this.tribe]}; - // For each tribe, get its spreadsheet, set the state - Object.keys(urls).map((tribe) => { - API.getSpreadsheet(urls[tribe], function(data) { - self.setState({mugic: Object.assign(self.state.mugic, {[tribe]: data})}); - }); - }); - } - render() { + if (this.props.children) { + return (
{this.props.children}
); + } + return ( + + ); + } + + fakerender() { if (this.props.children) { return (
{this.props.children}
); } diff --git a/src/components/portal/Single/Creature.js b/src/components/portal/Single/Creature.js index 5c58ffa..5172fac 100644 --- a/src/components/portal/Single/Creature.js +++ b/src/components/portal/Single/Creature.js @@ -1,38 +1,27 @@ import React from 'react'; import Interactive from 'react-interactive'; import { Link } from 'react-router'; -// import {browserHistory} from 'react-router'; import PageNotFound from '../../PageNotFound'; -import UnderConstruction from '../../UnderConstruction'; import API from '../../SpreadsheetData'; import s from '../../../styles/app.style'; +import {observer, inject} from 'mobx-react'; +@inject((stores, props, context) => props) @observer export default class SingleCreature extends React.Component { - constructor(props) { - super (props); - this.state = {tribe: '', creature: null, card_data: null}; - } - - componentWillReceiveProps(nextProps) { - this.getData(nextProps); - } - - componentDidMount() { - this.getData(this.props); - } - // ** Process the tribe ** // // /portal/Creatures/{Tribe}/{Name} // /portal/{Tribe}/Creatures/{Name} // The first / gets counted - getData(props) { - let path = props.location.pathname.split("/"); + render() { + const store = API; + + let path = this.props.location.pathname.split("/"); if (path[path.length-1] == "") path.pop(); // Remove trailing backslash // Path too long if ( path.length !== 5 ) { - return; + return(); } //Handle both url layouts @@ -40,86 +29,64 @@ export default class SingleCreature extends React.Component { if (path[2] === "Creatures") return path[3]; if (path[3] === "Creatures") return path[2]; })(); - this.setState({tribe: tribe}); - var name = decodeURIComponent(path[4]); - - var self = this; - API.getSpreadsheet(API.Creatures[tribe], (data) => { - data.map((item, i) => { - if (item.title.$t == name) - self.setState({creature: item }); - }); - // If no creature set as false - if (!self.state.creature) { - self.setState({creature: "n/a"}); - } - }); - API.getSpreadsheet(API.Creatures_Card_Data, (data) => { - data.map((item, i) => { - if (item.title.$t == name) self.setState({card_data: item }); - }); - // If no card_data set as false - if (!self.state.card_data) { - self.setState({card_data: "n/a"}); - } - }); - } - - render() { - var self = this; - - // Get spreadsheet data based on tribe/name - if (!(API.Creatures).hasOwnProperty(this.state.tribe)) { - return( - - //return(browserHistory.push('/PageNotFound')); - ); + if (store.urls === null || + store.portal === null || + store.cards === null) { + return (Loading...); } - // creature is the object to be used in the jsx - var creature = this.state.creature; - var card_data = this.state.card_data; + // Todo this isn't needed for now (handled by routes) + // if (!store.urls.Creatures.hasOwnProperty(tribe)) { + // return (Invalid Tribe: {tribe}); + // } - // TODO separate loading of card_data - if (creature == "n/a" || card_data == "n/a") return( - - ); + if (!store.cards.built.includes("creatures_Cards")) { + store.cards.setupCreatures("Cards"); + return (Loading...); + } - if (creature == null || card_data == null) return( - Loading... - ); + if (!store.portal.built.includes("creatures_"+tribe)) { + store.portal.setupCreatures(tribe); + return (Loading...); + } - const elements = card_data.gsx$elements.$t.split(/[ ,]+/).map((item, i) => { - return {item}; - }); + const creature = store.portal.creatures.findOne({'gsx$name': path[4]}); + const card_data = store.cards.creatures.findOne({'gsx$name': path[4]}); + if (!creature) { + return(); + } - const locations = creature.gsx$location.$t.split(/[,]+\s*/).map((item, i) => { + const locations = creature.gsx$location.split(/[,]+\s*/).map((item, i) => { return

{item}

; }); - const battlegear = creature.gsx$battlegear.$t.split(/[,]+\s*/).map((item, i) => { + const battlegear = creature.gsx$battlegear.split(/[,]+\s*/).map((item, i) => { return

{item}

; }); - return( -
-

{creature.gsx$name.$t}

- + const elements = card_data.gsx$elements.split(/[ ,]+/).map((item, i) => { + return {item}; + }); + + return ( +
+

{creature.gsx$name}

+
Appearance:
- {creature.gsx$appearance.$t} + {creature.gsx$appearance}

Background:
- {creature.gsx$background.$t} + {creature.gsx$background}

Details:
- {creature.gsx$details.$t} + {creature.gsx$details}

@@ -134,62 +101,72 @@ export default class SingleCreature extends React.Component {
Height (ft):
- {creature.gsx$height.$t} + {creature.gsx$height}

Special Abilities:
- {creature.gsx$specialabilities.$t} + {creature.gsx$specialabilities}

Weight (lb):
- {creature.gsx$weight.$t} + {creature.gsx$weight} +
+
+
+ Special Abilities:
+ {creature.gsx$specialabilities} +
+
+
+ Weight (lb):
+ {creature.gsx$weight}

Card ID: - {card_data.gsx$cardid.$t} + {card_data.gsx$cardid}

Set: - {card_data.gsx$set.$t} + {card_data.gsx$set}

Rarity: - {card_data.gsx$rarity.$t} + {card_data.gsx$rarity}

- Tribe: {this.state.tribe} - + Tribe: {tribe} +

Ability:
- {card_data.gsx$ability.$t} + {card_data.gsx$ability}

Courage: - {card_data.gsx$courage.$t} + {card_data.gsx$courage}

Power: - {card_data.gsx$power.$t} + {card_data.gsx$power}

Speed: - {card_data.gsx$speed.$t} + {card_data.gsx$speed}

Wisdom: - {card_data.gsx$wisdom.$t} + {card_data.gsx$wisdom}

@@ -198,17 +175,17 @@ export default class SingleCreature extends React.Component {
Energy: - {card_data.gsx$energy.$t} + {card_data.gsx$energy}

Flavortext:
- {card_data.gsx$flavortext.$t} + {card_data.gsx$flavortext}

Mugic Ability: - {card_data.gsx$mugicability.$t} + {card_data.gsx$mugicability}
); diff --git a/src/index.js b/src/index.js index 56d20d9..edcd011 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,9 @@ import UnderConstruction from './components/UnderConstruction'; import ExampleComponent from './components/ExampleComponent'; import ExampleTwoDeepComponent from './components/ExampleTwoDeepComponent'; +/* SpreadsheetData */ +import API from './components/SpreadsheetData'; + /* Home Page */ import Home from './components/Home'; @@ -43,6 +46,8 @@ const routes = ( {/* Collection */} + + {/* Portal */} @@ -168,6 +173,8 @@ const routes = ( + + diff --git a/webpack.config.babel.js b/webpack.config.babel.js index 578dfe0..0b45362 100644 --- a/webpack.config.babel.js +++ b/webpack.config.babel.js @@ -19,6 +19,10 @@ export default { extensions: ['', '.js', '.jsx'], }, + node: { + fs: 'empty' + }, + plugins: process.argv.indexOf('-p') === -1 ? null : [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'),