updated portal code to use new splash art imgur

This commit is contained in:
Daniel 2021-10-10 12:57:46 -04:00
parent 8807d34bac
commit 94ee014aeb
10 changed files with 610 additions and 593 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,190 +1,209 @@
import 'whatwg-fetch';
import { observable } from "mobx";
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';
function retry (fn: () => any, retries=0, err=null) {
if (retries < 0) {
return Promise.reject(err);
}
return fn().catch((err: any) => {
return retry(fn, (retries - 1), err);
});
}
class API {
@observable portal;
@observable cards;
@observable urls;
private static instance: API;
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" }
// such secure, much wow
get key() {
return ["AIz", "aSy", "Bfq", "09-", "tBi", "78b", "nH6", "6f1", "Lkn", "zGD", "XM9", "Zu9", "JG0"].join("");
}
private constructor () {
// This sets up urls and kicks off db
try {
const urls = {};
spreadsheet_data.forEach(({ type, subtype, url }) => {
if (!urls[type]) urls[type] = {};
urls[type][subtype] = url;
});
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;
}
path(spreadsheetID: string) {
return `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetID}/values/Sheet1?key=${this.key}`;
}
async getSpreadsheetTime(spreadsheetId: string) {
const url = `https://content.googleapis.com/drive/v3/files/${spreadsheetId}?fields=modifiedTime&key=${this.key}`;
const response = await fetch(url);
if (response.status === 200) {
const json = await response.json();
if ("modifiedTime" in json) {
return Promise.resolve(json.modifiedTime);
}
}
return Promise.reject();
}
async getSpreadsheetData(spreadsheetId: string, doRetry: boolean = false) {
const url = this.path(spreadsheetId);
const cmd = async () => {
const response = await fetch(url);
if (response.status === 404) {
throw new Error("Can't Open File");
}
try {
const json = await response.json();
return json.values;
} catch (err) {
throw new Error(err);
}
};
return retry(cmd, doRetry ? 3 : 0);
}
// Wrapper that transforms spreadsheet data into expected object
async parseSpreadsheetData(spreadsheetId: string, cardType: string, doRetry: boolean = false) {
return this.getSpreadsheetData(spreadsheetId, doRetry)
.then((data: Array<Array<string>>) => {
if (data.length < 2) return [];
const header = data.shift()!.map((h: string) => h.toLowerCase().replace(" ", ""));
const cards = data.map((card: string[]) => {
const obj = { "gsx$type": cardType };
for (let i = 0; i < header.length; i++) {
obj[`gsx$${header[i]}`] = card[i];
}
return obj;
});
return cards;
});
}
// 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, reject);
}
else if ('portal' in item) {
return this.portal!.setupType(item.portal, resolve, reject);
}
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();
setTimeout(() => {
window.location.reload();
}, 300);
}
}
export default API;
import 'whatwg-fetch';
import { observable } from "mobx";
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';
function retry (fn: () => any, retries=0, err=null) {
if (retries < 0) {
return Promise.reject(err);
}
return fn().catch((err: any) => {
return retry(fn, (retries - 1), err);
});
}
class API {
@observable portal;
@observable cards;
@observable urls;
private static instance: API;
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" }
// such secure, much wow
get key() {
return ["AIz", "aSy", "Bfq", "09-", "tBi", "78b", "nH6", "6f1", "Lkn", "zGD", "XM9", "Zu9", "JG0"].join("");
}
private constructor () {
// This sets up urls and kicks off db
try {
const urls = {};
spreadsheet_data.forEach(({ type, subtype, url }) => {
if (!urls[type]) urls[type] = {};
urls[type][subtype] = url;
});
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;
}
path(spreadsheetID: string) {
return `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetID}/values/Sheet1?key=${this.key}`;
}
async getSpreadsheetTime(spreadsheetId: string) {
const url = `https://content.googleapis.com/drive/v3/files/${spreadsheetId}?fields=modifiedTime&key=${this.key}`;
const response = await fetch(url);
if (response.status === 200) {
const json = await response.json();
if ("modifiedTime" in json) {
return Promise.resolve(json.modifiedTime);
}
}
return Promise.reject();
}
async getSpreadsheetData(spreadsheetId: string, doRetry: boolean = false) {
const url = this.path(spreadsheetId);
const cmd = async () => {
const response = await fetch(url);
if (response.status === 404) {
throw new Error("Can't Open File");
}
try {
const json = await response.json();
return json.values;
} catch (err) {
throw new Error(err);
}
};
return retry(cmd, doRetry ? 3 : 0);
}
// Wrapper that transforms spreadsheet data into expected object
async parseSpreadsheetData(spreadsheetId: string, cardType: string, doRetry: boolean = false) {
return this.getSpreadsheetData(spreadsheetId, doRetry)
.then((data: Array<Array<string>>) => {
if (data.length < 2) return [];
const header = data.shift()!.map((h: string) => h.toLowerCase().replace(" ", ""));
const cards = data.map((card: string[]) => {
const obj = { "gsx$type": cardType };
for (let i = 0; i < header.length; i++) {
obj[`gsx$${header[i]}`] = card[i];
}
return obj;
});
return cards;
});
}
// 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, reject);
}
else if ('portal' in item) {
return this.portal!.setupType(item.portal, resolve, reject);
}
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;
}
}
hasFullart = (card: Card) => (
Boolean(card.gsx$if !== undefined && card.gsx$if !== '') ||
Boolean(card.gsx$splash !== undefined && card.gsx$splash !== '') ||
Boolean(card.gsx$alt !== undefined && card.gsx$alt !== '')
);
/* Wrapper for full art */
cardFullart = (card: Card) => {
if (card.gsx$if && card.gsx$if !== '') {
return card.gsx$if;
} else if (card.gsx$splash && card.gsx$splash !== '') {
return this.base_image + card.gsx$splash;
} else if (card.gsx$alt) {
return card.gsx$alt;
} 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();
setTimeout(() => {
window.location.reload();
}, 300);
}
}
export default API;

View File

@ -1,214 +1,214 @@
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';
import { SearchButton } from '../Snippets';
import { sortCardName, text_link, thumb_link } from './Category/common.tsx';
@inject((stores, props, context) => props) @observer
export default class SearchPortal extends React.Component {
@observable input;
@observable query;
constructor(props) {
super(props);
// this.search = this.search.bind(this);
this.query = this.input = decodeURIComponent(this.props.location.search.substr(1));
}
render() {
return (<div className="search">
<form onSubmit={this.search}>
<input type="text" value={this.query} autoFocus onChange={(e) => this.query = e.target.value} />
<button type="submit"><SearchButton /></button>
</form>
<DBSearch string={this.input}/>
</div>);
}
search = (event) => {
event.preventDefault();
event.stopPropagation();
this.props.history.push('/portal/Search/?'+encodeURIComponent(this.query));
this.input = this.query;
}
}
@inject((stores, props, context) => props) @observer
class DBSearch extends React.Component {
@observable loaded = false;
@observable loading = false;
constructor() {
super();
this.filter = new loki("filter.db");
}
render() {
if (this.loaded == false) {
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;
// No search
if (string == "") {
return (<div style={{ minHeight: '50px' }}></div>);
}
const filter = this.filter.addCollection('filter');
var pview = filter.addDynamicView('filter');
pview.applySimpleSort('gsx$name');
let attackResults = API.portal.attacks.chain();
let battlegearResults = API.portal.battlegear.chain();
let creatureResults = API.portal.creatures.chain();
let locationResults = API.portal.locations.chain();
let mugicResults = API.portal.mugic.chain();
// Attributes Background Details
attackResults = attackResults.find({ '$or': [
{ 'gsx$attributes': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Attributes Background Details
battlegearResults = battlegearResults.find({ '$or': [
{ 'gsx$attributes': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Appearance Background Special Abilities Details
creatureResults = creatureResults.find({ '$or': [
{ 'gsx$appearance': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$specialabilities': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Local Features Background Details
locationResults = locationResults.find({ '$or': [
{ 'gsx$localfeatures': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Background Details
mugicResults = mugicResults.find({ '$or': [
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
let temp;
temp = attackResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = battlegearResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = creatureResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = locationResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = mugicResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
let content = pview.data().map((val, i) => text_link(val, i));
this.filter.removeCollection('filter');
let header;
// This prioritizes names in the results
const names = [].concat(
API.portal.attacks.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.battlegear.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.creatures.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.locations.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.mugic.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.cards.attacks.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.battlegear.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.creatures.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.locations.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.mugic.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data()
)
.sort(sortCardName)
// dedupe fullart results
.filter((val, i, arr) => (i == 0 || val.gsx$name != arr[i - 1].gsx$name))
.map((val, i) => thumb_link(val, i));
// Check Artists
if (content.length == 0) {
const artists = [].concat(
API.cards.attacks.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.battlegear.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.creatures.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.locations.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data(),
API.cards.mugic.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where((obj) => {return (obj.gsx$splash != ('') )}).data()
)
.sort((a, b) => (a.gsx$name > b.gsx$name) ? 1 : -1)
.map((val, i) => text_link(val, i));
if (artists.length > 0) {
header = `Art contributed by ${string}:`;
content = artists;
}
else {
header = 'No Results Found';
}
}
else {
header = `Results containing ${string}:`;
}
return (<div className="results">
<hr />
{names.length > 0 && <>
<div className="entry_nav">{names}</div>
<hr />
</>}
<div>{header}</div>
{content}
</div>);
}
}
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';
import { SearchButton } from '../Snippets';
import { sortCardName, text_link, thumb_link } from './Category/common.tsx';
@inject((stores, props, context) => props) @observer
export default class SearchPortal extends React.Component {
@observable input;
@observable query;
constructor(props) {
super(props);
// this.search = this.search.bind(this);
this.query = this.input = decodeURIComponent(this.props.location.search.substr(1));
}
render() {
return (<div className="search">
<form onSubmit={this.search}>
<input type="text" value={this.query} autoFocus onChange={(e) => this.query = e.target.value} />
<button type="submit"><SearchButton /></button>
</form>
<DBSearch string={this.input}/>
</div>);
}
search = (event) => {
event.preventDefault();
event.stopPropagation();
this.props.history.push('/portal/Search/?'+encodeURIComponent(this.query));
this.input = this.query;
}
}
@inject((stores, props, context) => props) @observer
class DBSearch extends React.Component {
@observable loaded = false;
@observable loading = false;
constructor() {
super();
this.filter = new loki("filter.db");
}
render() {
if (this.loaded == false) {
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;
// No search
if (string == "") {
return (<div style={{ minHeight: '50px' }}></div>);
}
const filter = this.filter.addCollection('filter');
var pview = filter.addDynamicView('filter');
pview.applySimpleSort('gsx$name');
let attackResults = API.portal.attacks.chain();
let battlegearResults = API.portal.battlegear.chain();
let creatureResults = API.portal.creatures.chain();
let locationResults = API.portal.locations.chain();
let mugicResults = API.portal.mugic.chain();
// Attributes Background Details
attackResults = attackResults.find({ '$or': [
{ 'gsx$attributes': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Attributes Background Details
battlegearResults = battlegearResults.find({ '$or': [
{ 'gsx$attributes': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Appearance Background Special Abilities Details
creatureResults = creatureResults.find({ '$or': [
{ 'gsx$appearance': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$specialabilities': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Local Features Background Details
locationResults = locationResults.find({ '$or': [
{ 'gsx$localfeatures': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
// Background Details
mugicResults = mugicResults.find({ '$or': [
{ 'gsx$background': { '$regex': new RegExp(string, 'i') }},
{ 'gsx$details': { '$regex': new RegExp(string, 'i') }}
]});
let temp;
temp = attackResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = battlegearResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = creatureResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = locationResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
temp = mugicResults.data();
temp.forEach(function(v){ delete v.$loki });
filter.insert(temp);
let content = pview.data().map((val, i) => text_link(val, i));
this.filter.removeCollection('filter');
let header;
// This prioritizes names in the results
const names = [].concat(
API.portal.attacks.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.battlegear.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.creatures.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.locations.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.portal.mugic.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }}),
API.cards.attacks.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.battlegear.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.creatures.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.locations.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.mugic.chain()
.find({ 'gsx$name': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data()
)
.sort(sortCardName)
// dedupe fullart results
.filter((val, i, arr) => (i == 0 || val.gsx$name != arr[i - 1].gsx$name))
.map((val, i) => thumb_link(val, i));
// Check Artists
if (content.length == 0) {
const artists = [].concat(
API.cards.attacks.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.battlegear.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.creatures.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.locations.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data(),
API.cards.mugic.chain()
.find({ 'gsx$artist': { '$regex': new RegExp(string, 'i') }})
.where(API.hasFullart).data()
)
.sort((a, b) => (a.gsx$name > b.gsx$name) ? 1 : -1)
.map((val, i) => text_link(val, i));
if (artists.length > 0) {
header = `Art contributed by ${string}:`;
content = artists;
}
else {
header = 'No Results Found';
}
}
else {
header = `Results containing ${string}:`;
}
return (<div className="results">
<hr />
{names.length > 0 && <>
<div className="entry_nav">{names}</div>
<hr />
</>}
<div>{header}</div>
{content}
</div>);
}
}

View File

@ -52,7 +52,7 @@ export default class SingleAttack extends React.Component {
);
}
else if (card_data) {
if (card_data.gsx$splash) {
if (API.hasFullart(card_data)) {
return (<Single card={card_data}/>);
}
}

View File

@ -51,7 +51,7 @@ export default class SingleBattlegear extends React.Component {
/>);
}
else if (card_data) {
if (card_data.gsx$splash) {
if (API.hasFullart(card_data)) {
return (<Single card={card_data}/>);
}
}

View File

@ -1,178 +1,178 @@
import React from 'react';
import { Interactive } from 'react-interactive';
import { Link } from 'react-router-dom';
import API from '../../SpreadsheetData';
import s from '../../../styles/app.style';
import { observer, inject } from 'mobx-react';
import Single from './_base';
import { PageNotFound, Element, Mugic, Discipline, Tribe } from '../../Snippets';
function Artist(props) {
const artists = [];
props.artist.split(/(?=, )/).forEach((artist, i) => {
artists.push(<Link key={i} to={`/portal/Search/?${artist.replace(", ", "")}`}>{artist}</Link>);
});
return (<div className="ability">{artists}</div>);
}
@inject((stores, props, context) => props) @observer
export default class SingleCreature extends React.Component {
// ** Process the tribe ** //
// /portal/Creatures/{Tribe}/{Name}
// /portal/{Tribe}/Creatures/{Name}
// The first / gets counted
render() {
const path = this.props.location.pathname.split("/");
if (path[path.length-1] == "") path.pop(); // Remove trailing backslash
const name = (() => {
if (path.length >= 5) return decodeURIComponent(path[4]);
if (path.length == 4) return decodeURIComponent(path[3]);
})();
const creature = API.portal.creatures.findOne({ 'gsx$name': name });
const card_data = API.cards.creatures.findOne({ 'gsx$name': name });
if (creature) {
const tribe = creature.gsx$tribe;
const mugic = [];
for (let i = 0; i < parseInt(card_data.gsx$mugicability || 0); i++) {
mugic.push(<Mugic key={i} tribe={tribe} />);
}
const col2 = [];
if (creature.gsx$attributes) {
col2.push(["Appearance", creature.gsx$attributes]);
}
if (creature.gsx$background) {
col2.push(["Background", creature.gsx$background]);
}
if (creature.gsx$details) {
col2.push(["Details", creature.gsx$details]);
}
if (creature.gsx$battlegear) {
col2.push(["Favorite Battlegear(s)", creature.gsx$battlegear.split(/[;]+\s*/).map((item, i) =>
<p key={i}><Interactive as={Link} {...s.link} to={"/portal/Battlegear/"+item}><span>{item}</span></Interactive></p>
)]);
}
if (creature.gsx$location) {
col2.push(["Favorite Location(s)", creature.gsx$location.split(/[;]+\s*/).map((item, i) =>
<p key={i}><Interactive as={Link} {...s.link} to={"/portal/Locations/"+item}><span>{item}</span></Interactive></p>
)]);
}
if (creature.gsx$height) {
col2.push(["Height (ft)", creature.gsx$height]);
}
if (creature.gsx$specialabilities) {
col2.push(["Special Abilities", creature.gsx$specialabilities]);
}
if (creature.gsx$weight) {
col2.push(["Weight (lb)", creature.gsx$weight]);
}
return (<Single
card={card_data}
col0={<>
<div>
<strong>Tribe: </strong>
<Tribe tribe={tribe} />&nbsp;
{tribe}
</div>
<hr />
<div>
<strong>Disciplines: </strong>
{card_data.gsx$courage}<Discipline discipline="courage" />&nbsp;
{card_data.gsx$power}<Discipline discipline="power" />&nbsp;
{card_data.gsx$wisdom}<Discipline discipline="wisdom" />&nbsp;
{card_data.gsx$speed}<Discipline discipline="speed" />
</div>
<hr />
<div>
<strong>Energy: </strong>
{card_data.gsx$energy}
</div>
<hr />
<div>
<strong>Elements: </strong>
<Element element="fire" value={card_data.gsx$elements.toLowerCase().indexOf("fire") >=0} />&nbsp;
<Element element="air" value={card_data.gsx$elements.toLowerCase().indexOf("air") >=0} />&nbsp;
<Element element="earth" value={card_data.gsx$elements.toLowerCase().indexOf("earth") >=0} />&nbsp;
<Element element="water" value={card_data.gsx$elements.toLowerCase().indexOf("water") >=0} />
</div>
<hr />
<div>
<strong>Mugic Ability: </strong>
{mugic}
</div>
</>}
col2={
col2.map((val, i) => {
return (<React.Fragment key={i} >
<div>
<strong>{val[0]}:</strong><br />
{val[1]}
</div>
{i !== col2.length - 1 && <hr />}
</React.Fragment>);
})
}
/>);
}
else if (card_data) {
if (card_data.gsx$splash) {
const tribe = card_data.gsx$tribe;
const mugic = [];
for (let i = 0; i < parseInt(card_data.gsx$mugicability || 0); i++) {
mugic.push(<Mugic key={i} tribe={tribe} />);
}
return (<Single
card={card_data}
col0={<>
<div>
<strong>Tribe: </strong>
<Tribe tribe={tribe} />&nbsp;
{tribe}
</div>
<hr />
<div>
<strong>Disciplines: </strong>
{card_data.gsx$courage}
<Discipline discipline="courage" />&nbsp;
{card_data.gsx$power}
<Discipline discipline="power" />&nbsp;
{card_data.gsx$speed}
<Discipline discipline="speed" />&nbsp;
{card_data.gsx$wisdom}
<Discipline discipline="wisdom" />
</div>
<hr />
<div>
<strong>Energy: </strong>
{card_data.gsx$energy}
</div>
<hr />
<div>
<strong>Elements: </strong>
<Element element="fire" value={card_data.gsx$elements.toLowerCase().indexOf("fire") >=0} />&nbsp;
<Element element="air" value={card_data.gsx$elements.toLowerCase().indexOf("air") >=0} />&nbsp;
<Element element="earth" value={card_data.gsx$elements.toLowerCase().indexOf("earth") >=0} />&nbsp;
<Element element="water" value={card_data.gsx$elements.toLowerCase().indexOf("water") >=0} />
</div>
<hr />
<div>
<strong>Mugic Ability: </strong>
{mugic}
</div>
</>}
/>);
}
}
return (<PageNotFound location={this.props.location}/>);
}
}
import React from 'react';
import { Interactive } from 'react-interactive';
import { Link } from 'react-router-dom';
import API from '../../SpreadsheetData';
import s from '../../../styles/app.style';
import { observer, inject } from 'mobx-react';
import Single from './_base';
import { PageNotFound, Element, Mugic, Discipline, Tribe } from '../../Snippets';
function Artist(props) {
const artists = [];
props.artist.split(/(?=, )/).forEach((artist, i) => {
artists.push(<Link key={i} to={`/portal/Search/?${artist.replace(", ", "")}`}>{artist}</Link>);
});
return (<div className="ability">{artists}</div>);
}
@inject((stores, props, context) => props) @observer
export default class SingleCreature extends React.Component {
// ** Process the tribe ** //
// /portal/Creatures/{Tribe}/{Name}
// /portal/{Tribe}/Creatures/{Name}
// The first / gets counted
render() {
const path = this.props.location.pathname.split("/");
if (path[path.length-1] == "") path.pop(); // Remove trailing backslash
const name = (() => {
if (path.length >= 5) return decodeURIComponent(path[4]);
if (path.length == 4) return decodeURIComponent(path[3]);
})();
const creature = API.portal.creatures.findOne({ 'gsx$name': name });
const card_data = API.cards.creatures.findOne({ 'gsx$name': name });
if (creature) {
const tribe = creature.gsx$tribe;
const mugic = [];
for (let i = 0; i < parseInt(card_data.gsx$mugicability || 0); i++) {
mugic.push(<Mugic key={i} tribe={tribe} />);
}
const col2 = [];
if (creature.gsx$attributes) {
col2.push(["Appearance", creature.gsx$attributes]);
}
if (creature.gsx$background) {
col2.push(["Background", creature.gsx$background]);
}
if (creature.gsx$details) {
col2.push(["Details", creature.gsx$details]);
}
if (creature.gsx$battlegear) {
col2.push(["Favorite Battlegear(s)", creature.gsx$battlegear.split(/[;]+\s*/).map((item, i) =>
<p key={i}><Interactive as={Link} {...s.link} to={"/portal/Battlegear/"+item}><span>{item}</span></Interactive></p>
)]);
}
if (creature.gsx$location) {
col2.push(["Favorite Location(s)", creature.gsx$location.split(/[;]+\s*/).map((item, i) =>
<p key={i}><Interactive as={Link} {...s.link} to={"/portal/Locations/"+item}><span>{item}</span></Interactive></p>
)]);
}
if (creature.gsx$height) {
col2.push(["Height (ft)", creature.gsx$height]);
}
if (creature.gsx$specialabilities) {
col2.push(["Special Abilities", creature.gsx$specialabilities]);
}
if (creature.gsx$weight) {
col2.push(["Weight (lb)", creature.gsx$weight]);
}
return (<Single
card={card_data}
col0={<>
<div>
<strong>Tribe: </strong>
<Tribe tribe={tribe} />&nbsp;
{tribe}
</div>
<hr />
<div>
<strong>Disciplines: </strong>
{card_data.gsx$courage}<Discipline discipline="courage" />&nbsp;
{card_data.gsx$power}<Discipline discipline="power" />&nbsp;
{card_data.gsx$wisdom}<Discipline discipline="wisdom" />&nbsp;
{card_data.gsx$speed}<Discipline discipline="speed" />
</div>
<hr />
<div>
<strong>Energy: </strong>
{card_data.gsx$energy}
</div>
<hr />
<div>
<strong>Elements: </strong>
<Element element="fire" value={card_data.gsx$elements.toLowerCase().indexOf("fire") >=0} />&nbsp;
<Element element="air" value={card_data.gsx$elements.toLowerCase().indexOf("air") >=0} />&nbsp;
<Element element="earth" value={card_data.gsx$elements.toLowerCase().indexOf("earth") >=0} />&nbsp;
<Element element="water" value={card_data.gsx$elements.toLowerCase().indexOf("water") >=0} />
</div>
<hr />
<div>
<strong>Mugic Ability: </strong>
{mugic}
</div>
</>}
col2={
col2.map((val, i) => {
return (<React.Fragment key={i} >
<div>
<strong>{val[0]}:</strong><br />
{val[1]}
</div>
{i !== col2.length - 1 && <hr />}
</React.Fragment>);
})
}
/>);
}
else if (card_data) {
if (API.hasFullart(card_data)) {
const tribe = card_data.gsx$tribe;
const mugic = [];
for (let i = 0; i < parseInt(card_data.gsx$mugicability || 0); i++) {
mugic.push(<Mugic key={i} tribe={tribe} />);
}
return (<Single
card={card_data}
col0={<>
<div>
<strong>Tribe: </strong>
<Tribe tribe={tribe} />&nbsp;
{tribe}
</div>
<hr />
<div>
<strong>Disciplines: </strong>
{card_data.gsx$courage}
<Discipline discipline="courage" />&nbsp;
{card_data.gsx$power}
<Discipline discipline="power" />&nbsp;
{card_data.gsx$speed}
<Discipline discipline="speed" />&nbsp;
{card_data.gsx$wisdom}
<Discipline discipline="wisdom" />
</div>
<hr />
<div>
<strong>Energy: </strong>
{card_data.gsx$energy}
</div>
<hr />
<div>
<strong>Elements: </strong>
<Element element="fire" value={card_data.gsx$elements.toLowerCase().indexOf("fire") >=0} />&nbsp;
<Element element="air" value={card_data.gsx$elements.toLowerCase().indexOf("air") >=0} />&nbsp;
<Element element="earth" value={card_data.gsx$elements.toLowerCase().indexOf("earth") >=0} />&nbsp;
<Element element="water" value={card_data.gsx$elements.toLowerCase().indexOf("water") >=0} />
</div>
<hr />
<div>
<strong>Mugic Ability: </strong>
{mugic}
</div>
</>}
/>);
}
}
return (<PageNotFound location={this.props.location}/>);
}
}

View File

@ -58,7 +58,7 @@ export default class SingleLocation extends React.Component {
/>);
}
else if (card_data) {
if (card_data.gsx$splash) {
if (API.hasFullart(card_data)) {
return (<Single
card={card_data}
col0={<>

View File

@ -78,7 +78,7 @@ export default class SingleMugic extends React.Component {
/>);
}
else if (card_data) {
if (card_data.gsx$splash) {
if (API.hasFullart(card_data)) {
const tribe = card_data.gsx$tribe;
return (<Single

View File

@ -43,14 +43,12 @@ export default class Single extends React.Component {
return (<>
<div className={"modal" + (this.fullscreen?"":" hidden")}>
<span className="close" onClick={this.close}>&times;</span>
<img className="modal-content" src={API.base_image + card.gsx$splash} />
<img className="modal-content" src={API.cardFullart(card)} />
</div>
{card.gsx$splash && (
{API.hasFullart(card) && (
<div className="entry_splash">
{/*<span className="arrow">&#8681;</span>*/}
{card.gsx$splash && (
<img onClick={this.expand} src={API.base_image + card.gsx$splash} />
)}
<img onClick={this.expand} src={API.cardFullart(card)} />
</div>
)}
<div className="entry_body">