Webatrice: card import wizard (#4397)

This commit is contained in:
Jeremy Letto 2021-10-14 20:42:35 -05:00 committed by GitHub
parent dde0f568d9
commit 36e5a399d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1479 additions and 35 deletions

View file

@ -0,0 +1,104 @@
// Fetch and parse card sets
class CardImporterService {
importCards(url): Promise<any> {
const error = 'Card import must be in valid MTG JSON format';
return fetch(url)
.then(response => {
if (response.headers.get('Content-Type') !== 'application/json') {
throw new Error(error);
}
return response.json();
})
.then((json) => {
try {
const sortedSets = Object.keys(json.data)
.map(key => json.data[key])
.sort((a, b) => new Date(a.releaseDate).getTime() - new Date(b.releaseDate).getTime());
const sets = sortedSets.map(({ cards, tokens, ...set}) => ({
...set,
cards: cards.map(({ name }) => name),
tokens: tokens.map(({ name }) => name),
}));
const unsortedCards = sortedSets.reduce((acc, set) => {
set.cards.forEach(card => acc[card.name] = card);
return acc;
}, {});
const cards = Object.keys(unsortedCards)
.sort((a, b) => a.localeCompare(b))
.map(key => unsortedCards[key]);
return { cards, sets };
} catch (e) {
throw new Error(error);
}
});
}
importTokens(url): Promise<any> {
const error = 'Token import must be in valid MTG XML format';
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch');
}
return response.text()
})
.then((xmlString) => {
try {
const parser = new DOMParser();
const dom = parser.parseFromString(xmlString, "application/xml");
const tokens = Array.from(dom.querySelectorAll('card')).map(
(tokenElement) => this.parseXmlAttributes(tokenElement)
);
return tokens;
} catch (e) {
throw new Error(error);
}
})
}
private parseXmlAttributes(dom: Element) {
return Array.from(dom.children).reduce((attributes, child) => {
const value = child.children.length ? this.parseXmlAttributes(child) : child.innerHTML;
let parsedAttributes = { value };
if (child.attributes.length) {
const childAttributes = Array.from(child.attributes).reduce((acc, { name, value }) => {
acc[name] = value;
return acc;
}, {});
parsedAttributes = {
...parsedAttributes,
...childAttributes,
};
}
// @TODO: clean this up and normalize what i'm returning
if (attributes[child.tagName]) {
if (Array.isArray(attributes[child.tagName])) {
attributes[child.tagName].push(parsedAttributes)
} else {
attributes[child.tagName] = [ parsedAttributes ];
}
} else {
attributes[child.tagName] = parsedAttributes;
}
return attributes;
}, {});
}
}
export const cardImporterService = new CardImporterService();

View file

@ -0,0 +1,19 @@
import { Card } from 'types';
import { dexieService } from '../DexieService';
export class CardDTO extends Card {
save() {
return dexieService.cards.put(this);
}
static get(name) {
return dexieService.cards.where('name').equalsIgnoreCase(name).first();
}
static bulkAdd(cards: CardDTO[]): Promise<any> {
return dexieService.cards.bulkPut(cards);
}
};
dexieService.cards.mapToClass(CardDTO);

View file

@ -0,0 +1,19 @@
import { Set } from 'types';
import { dexieService } from '../DexieService';
export class SetDTO extends Set {
save() {
return dexieService.sets.put(this);
}
static get(name) {
return dexieService.sets.where('name').equalsIgnoreCase(name).first();
}
static bulkAdd(sets: SetDTO[]): Promise<any> {
return dexieService.sets.bulkPut(sets);
}
};
dexieService.cards.mapToClass(SetDTO);

View file

@ -0,0 +1,19 @@
import { Token } from 'types';
import { dexieService } from '../DexieService';
export class TokenDTO extends Token {
save() {
return dexieService.tokens.put(this);
}
static get(name) {
return dexieService.tokens.where('name.value').equalsIgnoreCase(name).first();
}
static bulkAdd(tokens: TokenDTO[]): Promise<any> {
return dexieService.tokens.bulkPut(tokens);
}
};
dexieService.tokens.mapToClass(TokenDTO);

View file

@ -0,0 +1,3 @@
export * from './CardDTO';
export * from './SetDTO';
export * from './TokenDTO';

View file

@ -0,0 +1,35 @@
import Dexie from 'dexie';
enum Stores {
CARDS = 'cards',
SETS = 'sets',
TOKENS = 'tokens',
}
const StoreKeyIndexes = {
[Stores.CARDS]: "name",
[Stores.SETS]: "code",
[Stores.TOKENS]: "name.value",
};
class DexieService {
private db: Dexie = new Dexie('Webatrice');
constructor() {
this.db.version(1).stores(StoreKeyIndexes);
}
get cards() {
return this.db.table(Stores.CARDS);
}
get sets() {
return this.db.table(Stores.SETS);
}
get tokens() {
return this.db.table(Stores.TOKENS);
}
}
export const dexieService = new DexieService();

View file

@ -0,0 +1,2 @@
export * from "./CardImporterService";
export * from './DexieDTOs';