mirror of
https://github.com/chaoticbackup/chaoticbackup.github.io.git
synced 2026-03-21 17:24:14 -05:00
add retry to fetching google sheets
This commit is contained in:
parent
d87403032b
commit
1359d840dc
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
@media only screen and (min-width:975px){.pack{max-width:90%}.pack .card{width:20%}}@media only screen and (max-width:975px){.pack{max-width:90%}.pack .card{width:40%}}.packsim input[type=number]::-webkit-inner-spin-button,.packsim input[type=number]::-webkit-outer-spin-button{opacity:1}.enterthecode .card{width:250px;height:350px}.pack{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin:auto}.pack .card{margin:5px}.pack .card:hover{width:250px;height:350px}.pack .card>div{width:150px;height:210px;background-size:cover;box-sizing:content-box;-webkit-transition:-webkit-transform 1s,opacity 1s,background 1s,width 1s,height 1s,font-size 1s,top 1s,left 1s;-webkit-border-radius:5px;-o-transition-property:width,height,-o-transform,background,font-size,opacity,top,left;-o-transition-duration:1s,1s,1s,1s,1s,1s,1s,1s;-moz-transition-property:width,height,-o-transform,background,font-size,opacity,top,left;-moz-transition-duration:1s,1s,1s,1s,1s,1s,1s,1s;transition-property:width,height,transform,background,font-size,opacity,top,left;transition-duration:1s,1s,1s,1s,1s,1s,1s,1s}.pack .card>div:hover{width:250px;height:350px}.pack .card.locations{width:150px;height:210px}.pack .card.locations:hover{width:250px;height:350px}.pack .card.locations div{width:210px;height:150px;transform-origin:50% 70%;transform:rotate(270deg)}.pack .card.locations div:hover{width:350px;height:250px}.pack .stats{height:100%;width:100%;text-align:left}.pack .stats span{position:relative;color:#000;display:block;left:10px;font-size:12px}.pack .stats span:first-of-type{top:130px}.pack .stats span:nth-of-type(2){top:128px}.pack .stats span:nth-of-type(3){top:126px}.pack .stats span:nth-of-type(4){top:124px}.pack .stats span:nth-of-type(5){text-align:right;top:125px;left:-11px;font-size:14px}.pack .card:hover .stats span{left:20px;font-size:14px;font-weight:700}.pack .card:hover .stats span:first-of-type{top:220px}.pack .card:hover .stats span:nth-of-type(2){top:229px}.pack .card:hover .stats span:nth-of-type(3){top:237px}.pack .card:hover .stats span:nth-of-type(4){top:245px}.pack .card:hover .stats span:nth-of-type(5){text-align:right;top:258px;left:-21px;font-size:20px}
|
||||
@media only screen and (min-width:975px){.pack{max-width:90%}.pack .card{width:20%}}@media only screen and (max-width:975px){.pack{max-width:90%}.pack .card{width:40%}}.packsim input[type=number]::-webkit-inner-spin-button,.packsim input[type=number]::-webkit-outer-spin-button{opacity:1}.enterthecode .card{height:350px;width:250px}.pack{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin:auto}.pack .card{margin:5px}.pack .card:hover{height:350px;width:250px}.pack .card>div{background-size:cover;-webkit-border-radius:5px;box-sizing:content-box;height:210px;-webkit-transition:-webkit-transform 1s,opacity 1s,background 1s,width 1s,height 1s,font-size 1s,top 1s,left 1s;-o-transition-duration:1s,1s,1s,1s,1s,1s,1s,1s;-moz-transition-duration:1s,1s,1s,1s,1s,1s,1s,1s;transition-duration:1s,1s,1s,1s,1s,1s,1s,1s;-o-transition-property:width,height,-o-transform,background,font-size,opacity,top,left;-moz-transition-property:width,height,-o-transform,background,font-size,opacity,top,left;transition-property:width,height,transform,background,font-size,opacity,top,left;width:150px}.pack .card>div:hover{height:350px;width:250px}.pack .card.locations{height:210px;width:150px}.pack .card.locations:hover{height:350px;width:250px}.pack .card.locations div{height:150px;transform:rotate(270deg);transform-origin:50% 70%;width:210px}.pack .card.locations div:hover{height:250px;width:350px}.pack .stats{height:100%;text-align:left;width:100%}.pack .stats span{color:#000;display:block;font-size:12px;left:10px;position:relative}.pack .stats span:first-of-type{top:130px}.pack .stats span:nth-of-type(2){top:128px}.pack .stats span:nth-of-type(3){top:126px}.pack .stats span:nth-of-type(4){top:124px}.pack .stats span:nth-of-type(5){font-size:14px;left:-11px;text-align:right;top:125px}.pack .card:hover .stats span{font-size:14px;font-weight:700;left:20px}.pack .card:hover .stats span:first-of-type{top:220px}.pack .card:hover .stats span:nth-of-type(2){top:229px}.pack .card:hover .stats span:nth-of-type(3){top:237px}.pack .card:hover .stats span:nth-of-type(4){top:245px}.pack .card:hover .stats span:nth-of-type(5){font-size:20px;left:-21px;text-align:right;top:258px}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
.icon14{height:14px;padding-bottom:2px}.icon16{height:16px}.icon20{height:20px}.icon24{height:24px}.bigger{font-size:14px}.name{-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all}.name .subname:before{content:"\a ";white-space:pre}.name .subname{font-size:13px;padding-bottom:4px;display:inherit}.lore{text-align:left;padding-left:10%;padding-right:10%}.lore .block,.lore .title{margin-top:6px;text-align:center;font-weight:700}.lore .title{font-size:18px}.lore .block{font-size:24px}.lore div{margin:0 0 6px;line-height:22px!important}.lore .set{padding-left:5%;padding-right:5%}.donate{margin-bottom:6px}.donate form a{border-bottom:none}@-webkit-keyframes love{to{-webkit-transform:scale(1.1)}}@-moz-keyframes love{to{-moz-transform:scale(1.1)}}@keyframes love{to{transform:scale(1.1)}}@media only screen and (min-width:975px){.with-love{color:#333}}.with-love div,.with-love span{font-size:14px!important;line-height:18px!important}.with-love div{padding-bottom:4px}.with-love .heart{font-size:1.4em;color:#ff79c6;-webkit-transform:scale(.9);-moz-transform:scale(.9);transform:scale(.9);-webkit-animation:love .5s linear infinite alternate-reverse;-moz-animation:love .5s infinite linear alternate-reverse;animation:love .5s linear infinite alternate-reverse}.with-love a{text-decoration:none;border-bottom:1px dotted}
|
||||
.icon14{height:14px;padding-bottom:2px}.icon16{height:16px}.icon20{height:20px}.icon24{height:24px}.bigger{font-size:14px}.name{-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all}.name .subname:before{content:"\a ";white-space:pre}.name .subname{display:inherit;font-size:13px;padding-bottom:4px}.lore{padding-left:10%;padding-right:10%;text-align:left}.lore .block,.lore .title{font-weight:700;margin-top:6px;text-align:center}.lore .title{font-size:18px}.lore .block{font-size:24px}.lore div{line-height:22px!important;margin:0 0 6px}.lore .set{padding-left:5%;padding-right:5%}.donate{margin-bottom:6px}.donate form a{border-bottom:none}@-webkit-keyframes love{to{-webkit-transform:scale(1.1)}}@-moz-keyframes love{to{-moz-transform:scale(1.1)}}@keyframes love{to{transform:scale(1.1)}}@media only screen and (min-width:975px){.with-love{color:#333}}.with-love div,.with-love span{font-size:14px!important;line-height:18px!important}.with-love div{padding-bottom:4px}.with-love .heart{-webkit-animation:love .5s linear infinite alternate-reverse;-moz-animation:love .5s linear infinite alternate-reverse;animation:love .5s linear infinite alternate-reverse;color:#ff79c6;font-size:1.4em;-webkit-transform:scale(.9);-moz-transform:scale(.9);transform:scale(.9)}.with-love a{border-bottom:1px dotted;text-decoration:none}
|
||||
|
|
@ -1 +1 @@
|
|||
(self.webpackChunkchaoticbackup=self.webpackChunkchaoticbackup||[]).push([[640],{8640:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(2122),a=n(6610),l=n(5991),o=n(379),c=n(6089),i=n(7608),s=n(7294),u=n(3727),f=n(2195);n(2188);function m(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=(0,i.Z)(e);if(t){var a=(0,i.Z)(this).constructor;n=Reflect.construct(r,arguments,a)}else n=r.apply(this,arguments);return(0,c.Z)(this,n)}}var h=function(){return s.createElement("a",{href:"https://github.com/chaoticbackup",className:"name",rel:"noreferrer noopener",target:"_blank"},"Chaotic Backup Project")},d=function(e){var t=e.block,n=e.text,r=e.sets;return s.createElement("div",{className:"lore"},s.createElement("div",{className:"block"},t),n.map((function(e,t){return s.createElement("div",{key:t,dangerouslySetInnerHTML:{__html:e}})})),r.map((function(e,t){if(e.text&&e.text.length>0)return s.createElement("div",{className:"set",key:t},s.createElement("div",{className:"title"},e.title),e.text.map((function(e,t){return s.createElement("div",{key:t},e)})))})))},p=function(e){(0,o.Z)(n,e);var t=m(n);function n(){var e;(0,a.Z)(this,n);for(var r=arguments.length,l=new Array(r),o=0;o<r;o++)l[o]=arguments[o];return(e=t.call.apply(t,[this].concat(l))).state={lore:[]},e}return(0,l.Z)(n,[{key:"componentDidMount",value:function(){var e=this;fetch("/public/json/starter_lore.json").then((function(e){return e.json()})).then((function(t){e.setState({lore:t})})).catch((function(){e.setState({lore:[{block:"Unable to load lore...",text:[]}]})}))}},{key:"render",value:function(){return s.createElement("div",null,s.createElement("br",null),s.createElement("div",{className:"with-love"},s.createElement("div",null,"Welcome to the ",s.createElement(h,null),"."),s.createElement("span",null,"Built by fans for fans."),s.createElement("br",null),s.createElement("br",null),s.createElement("div",null,"Made with ",s.createElement("span",{className:"heart"},"♥")," by",s.createElement("br",null),"Danude Sandstorm (Project Lead)",s.createElement("br",null),"Chiodosin1 (Database Contributions)",s.createElement("br",null),"Afjak and Blitser (Art and Knowledge)"),s.createElement("div",null,"Do you like the site? You can donate to support it!"),s.createElement("div",{className:"donate"},s.createElement(f.U9,null)),s.createElement("div",{className:"lore"},"We were unsatisfied with the options on how to search for cards. I took the design of the old Chaotic website and added my own modernizations. With an extensive lists of search options in the ",s.createElement(u.rU,{to:"/collection"},"collection"),", you'll find deck building mores streamlined than ever before. Chaotic is full of rich lore, but unfortunately the best database of official lore disapeared when the ",s.createElement(u.rU,{to:"/portal"},"Portal to Perim")," disapeared along with the official site. You can again explore the official lore and information!"),s.createElement("br",null),this.state.lore.length>0?this.state.lore.map((function(e,t){return s.createElement(d,(0,r.Z)({key:t},e))})):"Loading lore entries..."))}}]),n}(s.Component)}}]);
|
||||
"use strict";(self.webpackChunkchaoticbackup=self.webpackChunkchaoticbackup||[]).push([[640],{8640:(e,t,n)=>{n.r(t),n.d(t,{default:()=>u});var a=n(7462),l=n(5603),r=n(7294),o=n(3727),c=n(2195),i=(n(2188),function(){return r.createElement("a",{href:"https://github.com/chaoticbackup",className:"name",rel:"noreferrer noopener",target:"_blank"},"Chaotic Backup Project")}),s=function(e){var t=e.block,n=e.text,a=e.sets;return r.createElement("div",{className:"lore"},r.createElement("div",{className:"block"},t),n.map((function(e,t){return r.createElement("div",{key:t,dangerouslySetInnerHTML:{__html:e}})})),a.map((function(e,t){if(e.text&&e.text.length>0)return r.createElement("div",{className:"set",key:t},r.createElement("div",{className:"title"},e.title),e.text.map((function(e,t){return r.createElement("div",{key:t},e)})))})))},u=function(e){function t(){for(var t,n=arguments.length,a=new Array(n),l=0;l<n;l++)a[l]=arguments[l];return(t=e.call.apply(e,[this].concat(a))||this).state={lore:[]},t}(0,l.Z)(t,e);var n=t.prototype;return n.componentDidMount=function(){var e=this;fetch("/public/json/starter_lore.json").then((function(e){return e.json()})).then((function(t){e.setState({lore:t})})).catch((function(){e.setState({lore:[{block:"Unable to load lore...",text:[]}]})}))},n.render=function(){return r.createElement("div",null,r.createElement("br",null),r.createElement("div",{className:"with-love"},r.createElement("div",null,"Welcome to the ",r.createElement(i,null),"."),r.createElement("span",null,"Built by fans for fans."),r.createElement("br",null),r.createElement("br",null),r.createElement("div",null,"Made with ",r.createElement("span",{className:"heart"},"♥")," by",r.createElement("br",null),"Danude Sandstorm (Project Lead)",r.createElement("br",null),"Chiodosin1 (Database Contributions)",r.createElement("br",null),"Afjak and Blitser (Art and Knowledge)"),r.createElement("div",null,"Do you like the site? You can donate to support it!"),r.createElement("div",{className:"donate"},r.createElement(c.U9,null)),r.createElement("div",{className:"lore"},"We were unsatisfied with the options on how to search for cards. I took the design of the old Chaotic website and added my own modernizations. With an extensive lists of search options in the ",r.createElement(o.rU,{to:"/collection"},"collection"),", you'll find deck building mores streamlined than ever before. Chaotic is full of rich lore, but unfortunately the best database of official lore disapeared when the ",r.createElement(o.rU,{to:"/portal"},"Portal to Perim")," disapeared along with the official site. You can again explore the official lore and information!"),r.createElement("br",null),this.state.lore.length>0?this.state.lore.map((function(e,t){return r.createElement(s,(0,a.Z)({key:t},e))})):"Loading lore entries..."))},t}(r.Component)}}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
37
package-lock.json
generated
37
package-lock.json
generated
|
|
@ -3405,6 +3405,11 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/retry": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz",
|
||||
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g=="
|
||||
},
|
||||
"@types/scheduler": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||
|
|
@ -10174,12 +10179,12 @@
|
|||
}
|
||||
},
|
||||
"p-retry": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
|
||||
"integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
|
||||
"dev": true,
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz",
|
||||
"integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==",
|
||||
"requires": {
|
||||
"retry": "^0.12.0"
|
||||
"@types/retry": "^0.12.0",
|
||||
"retry": "^0.13.1"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
|
|
@ -11336,10 +11341,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
|
||||
"dev": true
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
|
||||
},
|
||||
"reusify": {
|
||||
"version": "1.0.4",
|
||||
|
|
@ -13225,6 +13229,15 @@
|
|||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"p-retry": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
|
||||
"integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"retry": "^0.12.0"
|
||||
}
|
||||
},
|
||||
"resolve-cwd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
|
||||
|
|
@ -13234,6 +13247,12 @@
|
|||
"resolve-from": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
"lokijs": "^1.5.12",
|
||||
"mobx": "^5.15.7",
|
||||
"mobx-react": "^6.3.1",
|
||||
"p-retry": "^4.6.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.14.0",
|
||||
"react-collapsible": "^2.8.3",
|
||||
|
|
|
|||
|
|
@ -1,274 +0,0 @@
|
|||
import 'whatwg-fetch';
|
||||
import loki from 'lokijs';
|
||||
import { observable, observe, action } from "mobx";
|
||||
import Cookies from 'universal-cookie';
|
||||
const cookies = new Cookies();
|
||||
|
||||
class CollectionDB {
|
||||
// Keeps track of what collections have been populated
|
||||
@observable building = {};
|
||||
|
||||
constructor(API, format) {
|
||||
this.api = API;
|
||||
this.format = format;
|
||||
this.setupDB(format);
|
||||
}
|
||||
|
||||
async getSpreadsheetData(spreadsheet, type, callback) {
|
||||
this.api.getSpreadsheet(spreadsheet, (data) => {
|
||||
callback(data.map((item) => {
|
||||
const temp = {};
|
||||
delete item.content;
|
||||
for (const key of Object.keys(item)) {
|
||||
temp[key] = item[key].$t;
|
||||
}
|
||||
temp["gsx$type"] = type;
|
||||
return temp;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// example format
|
||||
// this.setup(this.api.urls.Attacks["portal"], "Attack", (data) => {});
|
||||
@action
|
||||
async setupType(type, resolve) {
|
||||
if (this.building.hasOwnProperty(type)) {
|
||||
const uc_type = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
if (this.building[type].get() == "built") {
|
||||
// Check if data has been updated
|
||||
this.getSpreadsheetData(this.api.urls[uc_type][this.format], uc_type, (data) => {
|
||||
const cookie = cookies.get(`${this.format}_${type}`);
|
||||
if (cookie) {
|
||||
if ((new Date(data[0].updated)) > (new Date(cookie))) {
|
||||
this[type].clear();
|
||||
this[type].insert(data);
|
||||
cookies.set(`${this.format}_${type}`, data[0].updated, { path: '/' });
|
||||
}
|
||||
}
|
||||
else {
|
||||
cookies.set(`${this.format}_${type}`, data[0].updated, { path: '/' });
|
||||
}
|
||||
});
|
||||
return resolve();
|
||||
}
|
||||
if (this.building[type].get() == "building") {
|
||||
const disposer = observe(this.building[type], (change) => {
|
||||
disposer();
|
||||
resolve();
|
||||
});
|
||||
return disposer;
|
||||
}
|
||||
if (this.building[type].get() == "setup") {
|
||||
this.building[type].set("building");
|
||||
// check if the collection already exists in memory
|
||||
if (this[type].data.length == 0) {
|
||||
return this.getSpreadsheetData(this.api.urls[uc_type][this.format], uc_type, (data) => {
|
||||
this[type].insert(data);
|
||||
this.building[type].set("built");
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.building[type].set("built");
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Wait until the database is initialized
|
||||
this.building[type] = observable.box("wait");
|
||||
const disposer = observe(this.building[type], () => {
|
||||
disposer();
|
||||
return this.setupType(type, resolve);
|
||||
});
|
||||
return disposer;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
setupDB(format) {
|
||||
const db = new loki(`chaotic_${format}.db`, {
|
||||
autosave: true,
|
||||
autoload: true,
|
||||
autoloadCallback: databaseInitialize.bind(this),
|
||||
autosaveInterval: 4000,
|
||||
persistenceMethod: 'localStorage'
|
||||
});
|
||||
|
||||
this.db = db;
|
||||
|
||||
function databaseInitialize() {
|
||||
["attacks","battlegear", "creatures", "locations", "mugic"]
|
||||
.forEach((type) => {
|
||||
// check if the db already exists in memory
|
||||
const entries = db.getCollection(type);
|
||||
if (entries === null || entries.data.length === 0) {
|
||||
this[type] = db.addCollection(type);
|
||||
if (this.building[type])
|
||||
this.building[type].set("setup");
|
||||
else
|
||||
this.building[type] = observable.box("setup");
|
||||
}
|
||||
else {
|
||||
this[type] = entries;
|
||||
if (this.building[type])
|
||||
this.building[type].set("built");
|
||||
else
|
||||
this.building[type] = observable.box("built");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
purgeDB = () => {
|
||||
this.db.deleteDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
class API {
|
||||
@observable portal = null;
|
||||
@observable cards = null;
|
||||
@observable urls = null;
|
||||
instance = null;
|
||||
|
||||
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
|
||||
get base_image() { return "https://drive.google.com/uc?id=" }
|
||||
get thumb_missing() { return "1JYjPzkv74IhzlHTyVh2niTDyui73HSfp" }
|
||||
get card_back() { return "https://i.imgur.com/xbeDBRJ.png" }
|
||||
|
||||
// Singleton
|
||||
static getInstance() {
|
||||
if (!this.instance) { this.instance = new API() }
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
static path(spreadsheetID) {
|
||||
return API.base_url + spreadsheetID + API.data_format;
|
||||
}
|
||||
|
||||
// Wrapper
|
||||
path(spreadsheetID) {
|
||||
return API.path(spreadsheetID);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.setupDB();
|
||||
}
|
||||
|
||||
async getSpreadsheet(spreadsheet, callback) {
|
||||
fetch(spreadsheet)
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
}).then((json) => {
|
||||
return callback(json.feed.entry);
|
||||
}).catch((err) => {
|
||||
console.error('parsing failed', err);
|
||||
return callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
// This sets up urls and kicks off db
|
||||
setupDB() {
|
||||
// let base_spreadsheet = "1cUNmwV693zl2zqbH_IG4Wz8o9Va_sOHe7pAZF6M59Es";
|
||||
try {
|
||||
const urls = {};
|
||||
const data = require('./meta_spreadsheet.json');
|
||||
// this.getSpreadsheet(API.path(API.base_spreadsheet), (data) => {
|
||||
// if (data == null) throw "no data from base_spreadsheet";
|
||||
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.portal = new CollectionDB(this, 'portal');
|
||||
this.cards = new CollectionDB(this, 'cards');
|
||||
}
|
||||
catch (err) {
|
||||
console.error('setting up database failed', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Input format
|
||||
// [{cards: 'attacks'}, {portal: 'attacks'}]
|
||||
async LoadDB(collection) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.urls !== null &&
|
||||
this.portal !== null &&
|
||||
this.cards !== null
|
||||
) {
|
||||
this.buildCollection(collection)
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
else resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// Input format
|
||||
// [{cards: 'attacks'}, {portal: 'attacks'}]
|
||||
async buildCollection(input) {
|
||||
return await Promise.all(input.map((item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if ('cards' in item)
|
||||
return this.cards.setupType(item.cards, resolve);
|
||||
if ('portal' in item)
|
||||
return this.portal.setupType(item.portal, resolve);
|
||||
console.error('cards or portal');
|
||||
return reject();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/* Wrappers for images */
|
||||
cardImage(card) {
|
||||
if (card.gsx$ic && card.gsx$ic !== '') {
|
||||
return card.gsx$ic;
|
||||
} else if (card.gsx$image && card.gsx$image !== '') {
|
||||
return this.base_image + card.gsx$image;
|
||||
} else {
|
||||
return this.card_back;
|
||||
}
|
||||
}
|
||||
|
||||
get tribes() {
|
||||
return ["Danian", "Generic", "Mipedian", "M'arrillian", "OverWorld", "UnderWorld"];
|
||||
}
|
||||
|
||||
// For the conversion of shorthand in database
|
||||
get sets() {
|
||||
return {
|
||||
"DOP": "Dawn of Perim",
|
||||
"ZOTH": "Zenith of the Hive",
|
||||
"SS": "Silent Sands",
|
||||
"MI": "Beyond the Doors",
|
||||
"ROTO": "Rise of the Oligarch",
|
||||
"TOTT": "Turn of the Tide",
|
||||
"FUN": "Forged Unity",
|
||||
"AU": "Alliance Unraveled",
|
||||
"FAS": "Fire and Stone",
|
||||
"OP1": "Organized Play 1",
|
||||
"PE1": "Premium Edition 1",
|
||||
"SAS": "Storm and Sea",
|
||||
"EE": "Elemental Emperors",
|
||||
"BR": "Beyond Rare",
|
||||
"LR": "League Rewards",
|
||||
"PROMO": "Promotional",
|
||||
"PROTO": "Prototype"
|
||||
};
|
||||
}
|
||||
|
||||
purgeDB() {
|
||||
this.cards.purgeDB();
|
||||
this.portal.purgeDB();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default API.getInstance();
|
||||
145
src/components/SpreadsheetData/API.ts
Normal file
145
src/components/SpreadsheetData/API.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import 'whatwg-fetch';
|
||||
import { observable } from "mobx";
|
||||
import pRetry from 'p-retry';
|
||||
import CollectionDB from './CollectionDB';
|
||||
import spreadsheet_data from './meta_spreadsheet.json';
|
||||
import { Card } from '../common/definitions';
|
||||
|
||||
type card_type = 'attacks' | 'battlegear' | 'creatures' | 'locations' | 'mugic';
|
||||
|
||||
type data_type = 'cards' | 'portal';
|
||||
|
||||
class API {
|
||||
@observable portal;
|
||||
@observable cards;
|
||||
@observable urls;
|
||||
private static instance: API;
|
||||
|
||||
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
|
||||
get base_image() { return "https://drive.google.com/uc?id=" }
|
||||
get thumb_missing() { return "1JYjPzkv74IhzlHTyVh2niTDyui73HSfp" }
|
||||
get card_back() { return "https://i.imgur.com/xbeDBRJ.png" }
|
||||
|
||||
private constructor () {
|
||||
// This sets up urls and kicks off db
|
||||
// let base_spreadsheet = "1cUNmwV693zl2zqbH_IG4Wz8o9Va_sOHe7pAZF6M59Es";
|
||||
try {
|
||||
const urls = {};
|
||||
// this.getSpreadsheet(API.path(API.base_spreadsheet), (data) => {
|
||||
// if (data == null) throw "no data from base_spreadsheet";
|
||||
spreadsheet_data.forEach((d) => {
|
||||
if (!urls[d.gsx$type.$t]) urls[d.gsx$type.$t] = {};
|
||||
urls[d.gsx$type.$t][d.gsx$subtype.$t] = this.path(d.gsx$url.$t);
|
||||
});
|
||||
this.urls = urls;
|
||||
// });
|
||||
|
||||
this.portal = new CollectionDB(this, 'portal');
|
||||
this.cards = new CollectionDB(this, 'cards');
|
||||
}
|
||||
catch (err) {
|
||||
this.portal = null;
|
||||
this.cards = null;
|
||||
this.urls = null;
|
||||
console.error('setting up database failed', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton
|
||||
static getInstance(): API {
|
||||
if (!API.instance) { API.instance = new API() }
|
||||
return API.instance;
|
||||
}
|
||||
|
||||
// Wrapper
|
||||
path(spreadsheetID: string) {
|
||||
return API.base_url + spreadsheetID + API.data_format;
|
||||
}
|
||||
|
||||
async getSpreadsheet(spreadsheet: string, retry: boolean, callback: (data: any) => any) {
|
||||
await pRetry(async () => {
|
||||
return fetch(spreadsheet)
|
||||
.then((response) => {
|
||||
if (response.status === 404) throw new Error("Can't Open File");
|
||||
return response.json();
|
||||
})
|
||||
.then((json) => {
|
||||
callback(json.feed.entry);
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new pRetry.AbortError(err);
|
||||
});
|
||||
}, { retries: retry ? 3 : 0 });
|
||||
}
|
||||
|
||||
// Input format
|
||||
// [{cards: 'attacks'}, {portal: 'attacks'}]
|
||||
async LoadDB(input: { [key in data_type]?: card_type }[]) {
|
||||
if (this.urls && this.portal && this.cards) {
|
||||
return Promise.all(input.map((item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if ('cards' in item) {
|
||||
return this.cards!.setupType(item.cards, resolve);
|
||||
}
|
||||
else if ('portal' in item) {
|
||||
return this.portal!.setupType(item.portal, resolve);
|
||||
}
|
||||
else {
|
||||
console.error('key must be cards or portal');
|
||||
return reject();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
else return Promise.reject();
|
||||
}
|
||||
|
||||
/* Wrappers for images */
|
||||
cardImage(card: Card) {
|
||||
if (card.gsx$ic && card.gsx$ic !== '') {
|
||||
return card.gsx$ic;
|
||||
} else if (card.gsx$image && card.gsx$image !== '') {
|
||||
return this.base_image + card.gsx$image;
|
||||
} else {
|
||||
return this.card_back;
|
||||
}
|
||||
}
|
||||
|
||||
get tribes() {
|
||||
return ["Danian", "Generic", "Mipedian", "M'arrillian", "OverWorld", "UnderWorld"];
|
||||
}
|
||||
|
||||
// For the conversion of shorthand in database
|
||||
get sets() {
|
||||
return {
|
||||
"DOP": "Dawn of Perim",
|
||||
"ZOTH": "Zenith of the Hive",
|
||||
"SS": "Silent Sands",
|
||||
"MI": "Beyond the Doors",
|
||||
"ROTO": "Rise of the Oligarch",
|
||||
"TOTT": "Turn of the Tide",
|
||||
"FUN": "Forged Unity",
|
||||
"AU": "Alliance Unraveled",
|
||||
"FAS": "Fire and Stone",
|
||||
"OP1": "Organized Play 1",
|
||||
"PE1": "Premium Edition 1",
|
||||
"SAS": "Storm and Sea",
|
||||
"EE": "Elemental Emperors",
|
||||
"BR": "Beyond Rare",
|
||||
"LR": "League Rewards",
|
||||
"PROMO": "Promotional",
|
||||
"PROTO": "Prototype"
|
||||
};
|
||||
}
|
||||
|
||||
purgeDB() {
|
||||
if (this.cards) this.cards.purgeDB();
|
||||
if (this.portal) this.portal.purgeDB();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default API;
|
||||
128
src/components/SpreadsheetData/CollectionDB.js
Normal file
128
src/components/SpreadsheetData/CollectionDB.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import loki from 'lokijs';
|
||||
import { observable, observe, action } from "mobx";
|
||||
import Cookies from 'universal-cookie';
|
||||
const cookies = new Cookies();
|
||||
|
||||
class CollectionDB {
|
||||
// Keeps track of what collections have been populated
|
||||
@observable building = {};
|
||||
|
||||
constructor(API, format) {
|
||||
this.api = API;
|
||||
this.format = format;
|
||||
this.setupDB(format);
|
||||
}
|
||||
|
||||
// Wrapper that transforms spreadsheet data into expected object
|
||||
async getSpreadsheetData(spreadsheet, type, retry, callback) {
|
||||
this.api.getSpreadsheet(spreadsheet, retry, (data) => {
|
||||
callback(data.map((item) => {
|
||||
const temp = {};
|
||||
delete item.content;
|
||||
for (const key of Object.keys(item)) {
|
||||
temp[key] = item[key].$t;
|
||||
}
|
||||
temp["gsx$type"] = type;
|
||||
return temp;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// example format
|
||||
// this.setup(this.api.urls.Attacks["portal"], "Attack", (data) => {});
|
||||
@action
|
||||
async setupType(type, resolve) {
|
||||
if (this.building.hasOwnProperty(type)) {
|
||||
const uc_type = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
if (this.building[type].get() == "built") {
|
||||
// Check if data has been updated
|
||||
this.getSpreadsheetData(this.api.urls[uc_type][this.format], uc_type, false, (data) => {
|
||||
const cookie = cookies.get(`${this.format}_${type}`);
|
||||
if (cookie) {
|
||||
if ((new Date(data[0].updated)) > (new Date(cookie))) {
|
||||
this[type].clear();
|
||||
this[type].insert(data);
|
||||
cookies.set(`${this.format}_${type}`, data[0].updated, { path: '/' });
|
||||
}
|
||||
}
|
||||
else {
|
||||
cookies.set(`${this.format}_${type}`, data[0].updated, { path: '/' });
|
||||
}
|
||||
});
|
||||
return resolve();
|
||||
}
|
||||
if (this.building[type].get() == "building") {
|
||||
const disposer = observe(this.building[type], (change) => {
|
||||
disposer();
|
||||
resolve();
|
||||
});
|
||||
return disposer;
|
||||
}
|
||||
if (this.building[type].get() == "setup") {
|
||||
this.building[type].set("building");
|
||||
// check if the collection already exists in memory
|
||||
if (this[type].data.length == 0) {
|
||||
return this.getSpreadsheetData(this.api.urls[uc_type][this.format], uc_type, true, (data) => {
|
||||
this[type].insert(data);
|
||||
this.building[type].set("built");
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.building[type].set("built");
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Wait until the database is initialized
|
||||
this.building[type] = observable.box("wait");
|
||||
const disposer = observe(this.building[type], () => {
|
||||
disposer();
|
||||
return this.setupType(type, resolve);
|
||||
});
|
||||
return disposer;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
setupDB(format) {
|
||||
const db = new loki(`chaotic_${format}.db`, {
|
||||
autosave: true,
|
||||
autoload: true,
|
||||
autoloadCallback: databaseInitialize.bind(this),
|
||||
autosaveInterval: 4000,
|
||||
persistenceMethod: 'localStorage'
|
||||
});
|
||||
|
||||
this.db = db;
|
||||
|
||||
function databaseInitialize() {
|
||||
["attacks","battlegear", "creatures", "locations", "mugic"]
|
||||
.forEach((type) => {
|
||||
// check if the db already exists in memory
|
||||
const entries = db.getCollection(type);
|
||||
if (entries === null || entries.data.length === 0) {
|
||||
this[type] = db.addCollection(type);
|
||||
if (this.building[type])
|
||||
this.building[type].set("setup");
|
||||
else
|
||||
this.building[type] = observable.box("setup");
|
||||
}
|
||||
else {
|
||||
this[type] = entries;
|
||||
if (this.building[type])
|
||||
this.building[type].set("built");
|
||||
else
|
||||
this.building[type] = observable.box("built");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
purgeDB = () => {
|
||||
this.db.deleteDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
export default CollectionDB;
|
||||
3
src/components/SpreadsheetData/index.ts
Normal file
3
src/components/SpreadsheetData/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import API from './API';
|
||||
|
||||
export default API.getInstance();
|
||||
|
|
@ -9,6 +9,7 @@ import search_api from './search';
|
|||
@inject((stores, props, context) => props) @observer
|
||||
export default class SearchCollection extends React.Component {
|
||||
@observable loaded = false;
|
||||
@observable loading = false;
|
||||
@observable input;
|
||||
@observable collapsed;
|
||||
list = ["sets", "types", "rarity", "tribes", "elements", "mull", "gender"];
|
||||
|
|
@ -187,12 +188,16 @@ export default class SearchCollection extends React.Component {
|
|||
|
||||
render() {
|
||||
if (this.loaded == false) {
|
||||
API.LoadDB([{ 'cards': 'attacks' }, { 'cards': 'battlegear' }, { 'cards': 'creatures' }, { 'cards': 'locations' }, { 'cards': 'mugic' }])
|
||||
if (this.loading == false) {
|
||||
this.loading = true;
|
||||
API.LoadDB([{ 'cards': 'attacks' }, { 'cards': 'battlegear' }, { 'cards': 'creatures' }, { 'cards': 'locations' }, { 'cards': 'mugic' }])
|
||||
.then(() => {
|
||||
this.loaded = true;
|
||||
this.loading = false;
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
return (<Loading />);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const EnterTheCode = () => {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
API.getSpreadsheet(API.path("1hzSojB76Me-P1qppxYR0oiHSU56jyK59x3DKm660ntc"), (data: any) => {
|
||||
API.getSpreadsheet(API.path("1hzSojB76Me-P1qppxYR0oiHSU56jyK59x3DKm660ntc"), false, (data: any) => {
|
||||
setFanData(data);
|
||||
});
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { sortCardName, thumb_link } from './common';
|
|||
@inject((stores, props, context) => props) @observer
|
||||
export default class Tribes extends React.Component {
|
||||
@observable loaded = false;
|
||||
@observable loading = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -25,11 +26,15 @@ export default class Tribes extends React.Component {
|
|||
// -> /{Tribe}/Mugic || /{Tribe}/Creatures
|
||||
render() {
|
||||
if (this.loaded == false) {
|
||||
API.LoadDB([{ 'cards': 'creatures' }, { 'portal': 'creatures' }, { 'cards': 'mugic' }, { 'portal': 'mugic' }])
|
||||
.then(() => {
|
||||
this.loaded = true;
|
||||
})
|
||||
.catch(() => {});
|
||||
if (this.loading == false) {
|
||||
this.loading = true;
|
||||
API.LoadDB([{ 'cards': 'creatures' }, { 'portal': 'creatures' }, { 'cards': 'mugic' }, { 'portal': 'mugic' }])
|
||||
.then(() => {
|
||||
this.loaded = true;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
return (<Loading />);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { sortCardName, thumb_link } from './common';
|
|||
@inject((stores, props, context) => props) @observer
|
||||
export default class Category extends React.Component {
|
||||
@observable loaded = false;
|
||||
@observable loading = false;
|
||||
|
||||
scrollLeft(amount) {
|
||||
document.getElementsByClassName('bottom_nav')[0].scrollLeft = (amount);
|
||||
|
|
@ -19,11 +20,15 @@ export default class Category extends React.Component {
|
|||
const type = this.props.type.toLowerCase();
|
||||
|
||||
if (this.loaded == false) {
|
||||
API.LoadDB([{ 'cards': type }, { 'portal': type }])
|
||||
.then(() => {
|
||||
this.loaded = true;
|
||||
})
|
||||
.catch(() => {});
|
||||
if (this.loading == false) {
|
||||
this.loading = true;
|
||||
API.LoadDB([{ 'cards': type }, { 'portal': type }])
|
||||
.then(() => {
|
||||
this.loaded = true;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
return (<Loading />);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import API from '../SpreadsheetData';
|
||||
import { Loading } from '../Snippets';
|
||||
import { observable } from "mobx";
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import loki from 'lokijs';
|
||||
|
|
@ -39,6 +40,7 @@ export default class SearchPortal extends React.Component {
|
|||
@inject((stores, props, context) => props) @observer
|
||||
class DBSearch extends React.Component {
|
||||
@observable loaded = false;
|
||||
@observable loading = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -47,14 +49,18 @@ class DBSearch extends React.Component {
|
|||
|
||||
render() {
|
||||
if (this.loaded == false) {
|
||||
API.LoadDB([
|
||||
{ 'portal': 'attacks' }, { 'portal': 'battlegear' }, { 'portal': 'creatures' }, { 'portal': 'locations' }, { 'portal': 'mugic' },
|
||||
{ 'cards': 'attacks' }, { 'cards': 'battlegear' }, { 'cards': 'creatures' }, { 'cards': 'locations' }, { 'cards': 'mugic' }
|
||||
]).then(() => {
|
||||
this.loaded = true;
|
||||
})
|
||||
.catch(() => {});
|
||||
return (<span>Loading...</span>);
|
||||
if (this.loading == false) {
|
||||
this.loading = true;
|
||||
API.LoadDB([
|
||||
{ 'portal': 'attacks' }, { 'portal': 'battlegear' }, { 'portal': 'creatures' }, { 'portal': 'locations' }, { 'portal': 'mugic' },
|
||||
{ 'cards': 'attacks' }, { 'cards': 'battlegear' }, { 'cards': 'creatures' }, { 'cards': 'locations' }, { 'cards': 'mugic' }
|
||||
]).then(() => {
|
||||
this.loaded = true;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
return (<Loading />);
|
||||
}
|
||||
|
||||
const { string } = this.props;
|
||||
|
|
|
|||
|
|
@ -84,14 +84,10 @@ export default class SingleCreature extends React.Component {
|
|||
<hr />
|
||||
<div>
|
||||
<strong>Disciplines: </strong>
|
||||
{card_data.gsx$courage}
|
||||
<Discipline discipline="courage" />
|
||||
{card_data.gsx$power}
|
||||
<Discipline discipline="power" />
|
||||
{card_data.gsx$speed}
|
||||
<Discipline discipline="speed" />
|
||||
{card_data.gsx$wisdom}
|
||||
<Discipline discipline="wisdom" />
|
||||
{card_data.gsx$courage}<Discipline discipline="courage" />
|
||||
{card_data.gsx$power}<Discipline discipline="power" />
|
||||
{card_data.gsx$wisdom}<Discipline discipline="wisdom" />
|
||||
{card_data.gsx$speed}<Discipline discipline="speed" />
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user