Add code for loading sets and saving them locally
This commit is contained in:
parent
19f2e16c41
commit
2677a51872
13 changed files with 243 additions and 7 deletions
|
@ -11,12 +11,14 @@
|
|||
"dependencies": {
|
||||
"buefy": "^0.8.2",
|
||||
"core-js": "^2.6.5",
|
||||
"idb": "^4.0.4",
|
||||
"register-service-worker": "^1.6.2",
|
||||
"vue": "^2.6.10",
|
||||
"vue-class-component": "^7.0.2",
|
||||
"vue-property-decorator": "^8.1.0",
|
||||
"vue-router": "^3.0.3",
|
||||
"vuex": "^3.0.1"
|
||||
"vuex": "^3.0.1",
|
||||
"vuex-class": "^0.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.11.0",
|
||||
|
|
41
src/App.vue
41
src/App.vue
|
@ -1,16 +1,33 @@
|
|||
<template>
|
||||
<main>
|
||||
<main v-if="loaded">
|
||||
<TopBar v-if="!isFullscreen" />
|
||||
<router-view />
|
||||
</main>
|
||||
<main class="loading-box" v-else>
|
||||
<h1 class="loading-message" v-if="loading">{{ loading }}</h1>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
main.loading-box {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1.loading-message {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import TopBar from "./components/Navigation/TopBar.vue";
|
||||
import { Action, Getter } from "vuex-class";
|
||||
import TopBar from "@/components/Navigation/TopBar.vue";
|
||||
import { loadSets } from "@/mlpccg/set";
|
||||
import { AppState } from "./store/types";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
|
@ -18,8 +35,26 @@ import TopBar from "./components/Navigation/TopBar.vue";
|
|||
}
|
||||
})
|
||||
export default class App extends Vue {
|
||||
@Action showLoading!: (msg: string) => void;
|
||||
@Action hideLoading!: () => void;
|
||||
@Action setLoaded!: (loaded: boolean) => void;
|
||||
@Getter loaded!: boolean;
|
||||
@Getter loading!: string | null;
|
||||
|
||||
private get isFullscreen(): boolean {
|
||||
return this.$route.meta && this.$route.meta.fullscreen;
|
||||
}
|
||||
|
||||
private mounted() {
|
||||
// Load all sets
|
||||
this.loadCards();
|
||||
}
|
||||
|
||||
private async loadCards() {
|
||||
this.showLoading("Downloading data for all sets");
|
||||
await loadSets();
|
||||
this.hideLoading();
|
||||
this.setLoaded(true);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -3,4 +3,13 @@
|
|||
@import "variables";
|
||||
@import "~bulma/sass/utilities/derived-variables";
|
||||
@import "~bulma";
|
||||
@import "~buefy/src/scss/buefy";
|
||||
@import "~buefy/src/scss/buefy";
|
||||
|
||||
html {
|
||||
scrollbar-color: #404245 #2f3132;
|
||||
background-color: #2a2c2e;
|
||||
}
|
||||
|
||||
body {
|
||||
color: white;
|
||||
}
|
5
src/mlpccg/card.ts
Normal file
5
src/mlpccg/card.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const imgBaseURL = "https://mcg.zyg.ovh/images/cards/";
|
||||
|
||||
export function cardImageURL(cardid: string): string {
|
||||
return `${imgBaseURL}${cardid}.webp`;
|
||||
}
|
35
src/mlpccg/database.ts
Normal file
35
src/mlpccg/database.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { DBSchema, openDB } from "idb";
|
||||
import { Card } from "./types";
|
||||
|
||||
interface CardDB extends DBSchema {
|
||||
card: {
|
||||
key: string;
|
||||
value: Card;
|
||||
indexes: {
|
||||
"by-set": string;
|
||||
"by-element": string[];
|
||||
"by-type": string;
|
||||
"by-cost": number;
|
||||
"by-power": number;
|
||||
"by-rarity": string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const cardDBName = "card-db";
|
||||
|
||||
export async function openCardDB() {
|
||||
return await openDB<CardDB>(cardDBName, 1, {
|
||||
upgrade(db) {
|
||||
const cardStore = db.createObjectStore("card", {
|
||||
keyPath: "ID"
|
||||
});
|
||||
cardStore.createIndex("by-set", "Set");
|
||||
cardStore.createIndex("by-element", "Element");
|
||||
cardStore.createIndex("by-type", "Type");
|
||||
cardStore.createIndex("by-cost", "Cost");
|
||||
cardStore.createIndex("by-power", "Power");
|
||||
cardStore.createIndex("by-rarity", "Rarity");
|
||||
}
|
||||
});
|
||||
}
|
47
src/mlpccg/set.ts
Normal file
47
src/mlpccg/set.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { SetFile } from "./types";
|
||||
import { openCardDB } from "./database";
|
||||
|
||||
const baseURL = "https://mcg.zyg.ovh/setdata/";
|
||||
const allSets = [
|
||||
"PR",
|
||||
"CN",
|
||||
"RR",
|
||||
"CS",
|
||||
"CG",
|
||||
"AD",
|
||||
"EO",
|
||||
"HM",
|
||||
"MT",
|
||||
"DE",
|
||||
"SB",
|
||||
"FF",
|
||||
"Promo"
|
||||
];
|
||||
|
||||
export async function loadSets() {
|
||||
const db = await openCardDB();
|
||||
const itemcount = await db.count("card");
|
||||
if (itemcount > 100) {
|
||||
// DB already filled, exit early
|
||||
return;
|
||||
}
|
||||
const sets = await Promise.all(allSets.map(set => downloadSet(set)));
|
||||
await Promise.all(
|
||||
sets.map(
|
||||
async set =>
|
||||
await Promise.all(
|
||||
set.Cards.map(async card => await db.put("card", card))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function downloadSet(setid: string): Promise<SetFile> {
|
||||
const setfile = await fetch(`${baseURL}${setid.toLowerCase()}.json`);
|
||||
const setdata: SetFile = await setfile.json();
|
||||
setdata.Cards = setdata.Cards.map(c => {
|
||||
c.Set = setid;
|
||||
return c;
|
||||
});
|
||||
return setdata;
|
||||
}
|
28
src/mlpccg/types.ts
Normal file
28
src/mlpccg/types.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
export type Rarity = "C" | "U" | "R" | "SR" | "UR" | "RR";
|
||||
|
||||
export type PowerRequirement = { [key: string]: number };
|
||||
|
||||
export interface SetFile {
|
||||
Name: string;
|
||||
Cards: Card[];
|
||||
}
|
||||
|
||||
export interface Card {
|
||||
ID: string;
|
||||
Set: string;
|
||||
Name: string;
|
||||
Subname: string;
|
||||
Element: string[];
|
||||
Keywords: string[];
|
||||
Traits: string[];
|
||||
Requirement?: PowerRequirement;
|
||||
Cost?: number;
|
||||
Power?: number;
|
||||
Type: string;
|
||||
Text: string;
|
||||
Rarity: Rarity;
|
||||
ProblemBonus?: number;
|
||||
ProblemOpponentPower?: number;
|
||||
ProblemRequirement?: PowerRequirement;
|
||||
Boosted?: Card;
|
||||
}
|
18
src/store/actions.ts
Normal file
18
src/store/actions.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { ActionTree } from "vuex";
|
||||
import { AppState } from "./types";
|
||||
|
||||
const actions: ActionTree<AppState, AppState> = {
|
||||
showLoading({ commit }, message: string) {
|
||||
commit("setLoading", message);
|
||||
},
|
||||
|
||||
hideLoading({ commit }) {
|
||||
commit("setLoading", "");
|
||||
},
|
||||
|
||||
setLoaded({ commit }, loaded: boolean) {
|
||||
commit("setLoaded", loaded);
|
||||
}
|
||||
};
|
||||
|
||||
export default actions;
|
17
src/store/getters.ts
Normal file
17
src/store/getters.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { GetterTree } from "vuex";
|
||||
import { AppState } from "./types";
|
||||
|
||||
const getters: GetterTree<AppState, AppState> = {
|
||||
loaded(state): boolean {
|
||||
return state.loaded;
|
||||
},
|
||||
|
||||
loading(state): string | null {
|
||||
if (state.loading) {
|
||||
return state.loadingMessage;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default getters;
|
|
@ -4,8 +4,19 @@ import { AppState } from "./types";
|
|||
|
||||
Vue.use(Vuex);
|
||||
|
||||
import actions from "./actions";
|
||||
import mutations from "./mutations";
|
||||
import getters from "./getters";
|
||||
|
||||
const store: StoreOptions<AppState> = {
|
||||
state: {},
|
||||
state: {
|
||||
loaded: false,
|
||||
loading: false,
|
||||
loadingMessage: ""
|
||||
},
|
||||
actions,
|
||||
mutations,
|
||||
getters,
|
||||
modules: {}
|
||||
};
|
||||
|
||||
|
|
15
src/store/mutations.ts
Normal file
15
src/store/mutations.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { MutationTree } from "vuex";
|
||||
import { AppState } from "./types";
|
||||
|
||||
const mutations: MutationTree<AppState> = {
|
||||
setLoaded(state, loaded: boolean) {
|
||||
state.loaded = loaded;
|
||||
},
|
||||
|
||||
setLoading(state, message: string) {
|
||||
state.loading = message != "";
|
||||
state.loadingMessage = message;
|
||||
}
|
||||
};
|
||||
|
||||
export default mutations;
|
|
@ -1 +1,5 @@
|
|||
export interface AppState {}
|
||||
export interface AppState {
|
||||
loaded: boolean;
|
||||
loading: boolean;
|
||||
loadingMessage: string;
|
||||
}
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -4453,6 +4453,11 @@ icss-utils@^2.1.0:
|
|||
dependencies:
|
||||
postcss "^6.0.1"
|
||||
|
||||
idb@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/idb/-/idb-4.0.4.tgz#f0c06f58bd78fe557e4de944fd6ba6a3240faa8e"
|
||||
integrity sha512-ZYsaBSNub2yAnjvmRKudQlMIPqZQIefAOwNIPeXC+RLIeXYFc0UNQqONKNuQeBNf8oBOV5L75yJ9zFISjHVj4g==
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||
|
@ -8814,6 +8819,11 @@ vue@^2.6.10:
|
|||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
|
||||
integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==
|
||||
|
||||
vuex-class@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/vuex-class/-/vuex-class-0.3.2.tgz#c7e96a076c1682137d4d23a8dcfdc63f220e17a8"
|
||||
integrity sha512-m0w7/FMsNcwJgunJeM+wcNaHzK2KX1K1rw2WUQf7Q16ndXHo7pflRyOV/E8795JO/7fstyjH3EgqBI4h4n4qXQ==
|
||||
|
||||
vuex@^3.0.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.1.1.tgz#0c264bfe30cdbccf96ab9db3177d211828a5910e"
|
||||
|
|
Loading…
Reference in a new issue