Compare commits

..

No commits in common. "9616a2fe9705ee80c1331a997f4dc4be41136561" and "077806411c55417bbe7e44e571af6f1a11922c81" have entirely different histories.

10 changed files with 119 additions and 271 deletions

View file

@ -210,10 +210,10 @@ class FFTCGDB
logger.debug "getDecks: OK '#{userID}'" logger.debug "getDecks: OK '#{userID}'"
resolve (id: row.deck, content: JSON.parse row.json for row, i in rows) resolve (id: row.deck, content: JSON.parse row.json for row, i in rows)
delDeck: (userID, deckID) -> delDeck: (deckID) ->
new Promise (resolve, reject) => new Promise (resolve, reject) =>
stmt = @db.prepare 'DELETE FROM decks WHERE deck = ? AND user = ?' stmt = @db.prepare 'DELETE FROM decks WHERE deck = ?'
stmt.run [deckID, userID], (err) -> stmt.run [deckID], (err) ->
stmt.finalize() stmt.finalize()
if err if err
logger.warn "delDeck: FAIL db '#{err.code}' for '#{deckID}'" logger.warn "delDeck: FAIL db '#{err.code}' for '#{deckID}'"

View file

@ -1,34 +0,0 @@
logger = (require 'logging').default '/decks/add'
# session storage (volatile data)
session = (require '../../session')
# fftcg.db (persistent data)
fftcgdb = (require '../../db')
module.exports =
url: '/decks/add'
method: 'POST'
# schema: (require './modify.schema')
handler: (request, reply) ->
session.check request.body.session ? ""
.then (userid) ->
# active session found, get associated user
fftcgdb.addDeck (userid), (request.body.deckCards)
.then (deckID) ->
logger.info "OK user '#{userid}' added deck '#{deckID}'"
reply.send
success: true
deck: deckID
.catch (err) ->
# couldnt get user details
logger.warn "FAIL '#{err}' for user id '#{userid}'"
reply.send
success: false
.catch ->
# no session found
logger.info "FAIL '#{request.body.session}' session not found"
reply.send
success: false

View file

@ -1,34 +0,0 @@
logger = (require 'logging').default '/decks/delete'
# session storage (volatile data)
session = (require '../../session')
# fftcg.db (persistent data)
fftcgdb = (require '../../db')
module.exports =
url: '/decks/delete'
method: 'POST'
# schema: (require './modify.schema')
handler: (request, reply) ->
session.check request.body.session ? ""
.then (userid) ->
# active session found, get associated user
fftcgdb.delDeck (userid), (request.body.deckID)
.then (deckID) ->
logger.info "OK user '#{userid}' deleted deck '#{deckID}'"
reply.send
success: true
deck: deckID
.catch (err) ->
# couldnt get user details
logger.warn "FAIL '#{err}' for user id '#{userid}'"
reply.send
success: false
.catch ->
# no session found
logger.info "FAIL '#{request.body.session}' session not found"
reply.send
success: false

View file

@ -22,12 +22,8 @@ fastify.route (require "./routes/#{route}") for route in [
'user/register' 'user/register'
# list decks # list decks
'decks/list' 'decks/list'
# add deck
'decks/add'
# modify deck # modify deck
'decks/modify' 'decks/modify'
# delete deck
'decks/delete'
] ]
# request logging # request logging

View file

@ -3,94 +3,67 @@
import CardsDB from '@/plugins/ffdecks' import CardsDB from '@/plugins/ffdecks'
export default class { export default class {
constructor(id) { constructor() {
this.id = id this.id = null
this.name = '' this.name = ''
this.note = '' this.note = ''
this.cards = [] this.cards = []
} }
populate() { from_plainObject(obj) {
for (let card of this.cards) { this.id = obj.id
card.dbentry = CardsDB[card.serial] this.name = obj.content.name
} this.note = obj.content.note
} this.cards = obj.content.cards
from_object(obj) {
if (obj) {
this.name = obj.name
this.note = obj.note
this.cards = obj.cards
}
}
plainObject() {
let plainCards = []
for (let card of this.cards) {
plainCards.push({
serial: card.serial,
count: card.count
})
}
return {
name: this.name,
note: this.note,
cards: plainCards
}
} }
from_deckList(str) { from_deckList(str) {
// select all lines containing card serial numbers // select all lines containing card serial numbers
let cardLinesRE = /^.*\b\d+-0*\d{1,3}[A-Z]?\b.*$/gm let cardLinesRE = /^.*\b\d-\d{3}[A-Z]?\b.*$/gm
let cardLines = str.match(cardLinesRE) let cardLines = str.match(cardLinesRE)
let cardCounts = {} let cardCounts = {}
if (cardLines) { cardLines.forEach(cardLine => {
for (let cardLine of cardLines) { // extract serial (guaranteed to be in here!)
// extract serial (guaranteed to be in here!) let serialRE = /\b(\d-\d{3})[A-Z]?\b/i
let serialRE = /\b(\d+)-0*(\d{1,3})[A-Z]?\b/i let serial = serialRE.exec(cardLine)[1]
let serial = serialRE.exec(cardLine)
// force format 'x-xxx'
serial = `${serial[1]}-${serial[2].padStart(3, '0')}`
// strip out serial number // strip out serial number
cardLine = cardLine.replace(serialRE, '') cardLine = cardLine.replace(serialRE, '')
let countREs = [ let countREs = [
// prioritize a count with "times" symbol *, x, × // prioritize a count with "times" symbol *, x, ×
/\b([0-9]+)(?:[*×]|[x]\b)/, /\b([0-9]+)(?:[*×]|[x]\b)/,
/(?:[*×]|\b[x])([0-9]+)\b/, /(?:[*×]|\b[x])([0-9]+)\b/,
// next priority: count with whitespace // next priority: count with whitespace
/\s+([0-9]+)\s+/, /\s+([0-9]+)\s+/,
/\s+([0-9]+)\b/, /\s+([0-9]+)\b/,
/\b([0-9]+)\s+/, /\b([0-9]+)\s+/,
// least priority: any simple number // least priority: any simple number
/\b([0-9]+)\b/ /\b([0-9]+)\b/
] ]
// fallback value // fallback value
let count = 1 let count = 1
for (let countRE of countREs) { for (let countRE of countREs) {
let data = countRE.exec(cardLine) let data = countRE.exec(cardLine)
if (data) { if (data) {
count = Number(data[1]) count = Number(data[1])
break break
}
} }
// count copies
if (!cardCounts[serial]) {
cardCounts[serial] = 0
}
cardCounts[serial] += count
} }
}
// count copies
if (!cardCounts[serial]) {
cardCounts[serial] = 0
}
cardCounts[serial] += count
})
// push card data into deck // push card data into deck
this.cards = [] this.cards = []
for (let serial in cardCounts) { for(let serial in cardCounts) {
this.cards.push({ this.cards.push({
serial: serial, serial: serial,
count: cardCounts[serial] count: cardCounts[serial]
@ -125,6 +98,14 @@ export default class {
} }
} }
plainObject() {
return {
name: this.name,
note: this.note,
cards: this.cards
}
}
parts() { parts() {
let retval = ['Forwards', 'Backups', 'Summons, Monsters & more'].map( let retval = ['Forwards', 'Backups', 'Summons, Monsters & more'].map(
item => ({ item => ({
@ -169,10 +150,10 @@ export default class {
lines.push('') lines.push('')
// list each deck part // list each deck part
for (let part of this.parts()) { for(let part of this.parts()) {
lines.push(`${part.heading} (${part.count}):`) lines.push(`${part.heading} (${part.count}):`)
for (let card of part.cards) for(let card of part.cards)
lines.push(`${card.count}x ${card.serial} "${card.dbentry.name}"`) lines.push(`${card.count}x ${card.serial} "${card.dbentry.name}"`)
lines.push('') lines.push('')
@ -184,4 +165,10 @@ export default class {
count() { count() {
return this.cards.reduce((total, card) => total + card.count, 0) return this.cards.reduce((total, card) => total + card.count, 0)
} }
populate() {
for (let card of this.cards) {
card.dbentry = CardsDB[card.serial]
}
}
} }

View file

@ -2,14 +2,14 @@
<v-list-tile avatar> <v-list-tile avatar>
<v-tooltip @input="booted = true" bottom> <v-tooltip @input="booted = true" bottom>
<template v-slot:activator="{ on }"> <template v-slot:activator="{ on }">
<v-list-tile-avatar v-on="on" style="cursor: zoom-in"> <v-list-tile-avatar v-on="on">
<svg <svg
x="0px" x="0px"
y="0px" y="0px"
width="16px" width="16px"
height="30px" height="30px"
viewBox="0 0 16 30" viewBox="0 0 16 30"
style="enable-background:new 0 0 16 30" style="enable-background:new 0 0 16 30;"
xml:space="preserve" xml:space="preserve"
> >
<polygon points="0,5 8,0 16,5 16,25 8,30 0,25" :fill="color" /> <polygon points="0,5 8,0 16,5 16,25 8,30 0,25" :fill="color" />

View file

@ -28,49 +28,15 @@
<v-btn fab absolute bottom right @click.native="editing = true"> <v-btn fab absolute bottom right @click.native="editing = true">
<v-icon>edit</v-icon> <v-icon>edit</v-icon>
</v-btn> </v-btn>
<v-dialog v-model="deleting" persistent>
<v-btn fab absolute bottom left slot="activator">
<v-icon>delete</v-icon>
</v-btn>
<v-card>
<v-card-title class="headline">
Really delete this deck?
</v-card-title>
<v-card-text>
Are you sure you want to delete this deck? This cannot be undone.
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="error" @click.native="deleting = false">
Cancel
</v-btn>
<v-btn
color="success"
@click.native="
deleting = false
delete_deck()
"
>
Confirm
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout> </v-layout>
</v-container> </v-container>
<DeckEditor v-if="editing" :deck="deck" @save="save_deck"> <DeckEditor
<v-btn color="error" @click.native="editing = false"> :visible="editing"
<v-icon>cancel</v-icon> :deck="deck"
cancel @close="editing = false"
</v-btn> @change="change_deck"
</DeckEditor> />
</v-expansion-panel-content> </v-expansion-panel-content>
</template> </template>
@ -94,8 +60,7 @@ export default {
}, },
data: () => ({ data: () => ({
editing: false, editing: false
deleting: false
}), }),
computed: { computed: {
@ -103,30 +68,18 @@ export default {
}, },
methods: { methods: {
save_deck(new_deck) { change_deck(new_deck) {
axios axios
.post('/decks/modify', { .post('/decks/modify', {
session: this.session, session: this.session,
deckID: this.deck.id, deckID: this.deck.id,
deckCards: new_deck.plainObject() deckCards: new_deck
})
.then(response => {
if (response.data.success) {
this.editing = false
this.$emit('change')
}
})
},
delete_deck() {
axios
.post('/decks/delete', {
session: this.session,
deckID: this.deck.id
}) })
.then(response => { .then(response => {
if (response.data.success) { if (response.data.success) {
this.$emit('change') this.$emit('change')
} else {
this.editing = true
} }
}) })
} }

View file

@ -1,17 +1,12 @@
<template> <template>
<v-expansion-panel v-model="open"> <v-expansion-panel>
<Deck <Deck
v-for="deck in linked" v-for="deck in linked"
:deck="deck" :deck="deck"
:key="deck.id" :key="deck.id"
@change="refresh_decks" @change="refresh_decks"
/> />
<NewDeck <NewDeck />
@change="
open = null
refresh_decks()
"
/>
</v-expansion-panel> </v-expansion-panel>
</template> </template>
@ -31,10 +26,6 @@ export default {
NewDeck NewDeck
}, },
data: () => ({
open: null
}),
asyncComputed: { asyncComputed: {
decks: { decks: {
get() { get() {
@ -59,9 +50,8 @@ export default {
let result = [] let result = []
for (let plainDeck of this.decks) { for (let plainDeck of this.decks) {
let deck = new DeckJS(plainDeck.id) let deck = new DeckJS()
deck.from_plainObject(plainDeck)
deck.from_object(plainDeck.content)
deck.populate() deck.populate()
result.push(deck) result.push(deck)

View file

@ -6,16 +6,13 @@
<v-container> <v-container>
<v-card flat> <v-card flat>
<DeckEditor ref="editor" @save="save_deck" /> <!-- <DeckEditor :visible="true" value="" @close="" @change="" /> -->
</v-card> </v-card>
</v-container> </v-container>
</v-expansion-panel-content> </v-expansion-panel-content>
</template> </template>
<script> <script>
import * as Cookies from 'js-cookie'
import axios from '@/plugins/axios'
import DeckEditor from './forms/DeckEditor.vue' import DeckEditor from './forms/DeckEditor.vue'
export default { export default {
@ -23,26 +20,6 @@ export default {
components: { components: {
DeckEditor DeckEditor
},
computed: {
session: () => Cookies.get('session')
},
methods: {
save_deck(new_deck) {
axios
.post('/decks/add', {
session: this.session,
deckCards: new_deck.plainObject()
})
.then(response => {
if (response.data.success) {
this.$emit('change')
this.$refs.editor.clear()
}
})
}
} }
} }
</script> </script>

View file

@ -1,5 +1,5 @@
<template> <template>
<v-container> <v-container v-if="visible">
<v-card flat> <v-card flat>
<v-alert :value="check.count !== 50" type="warning"> <v-alert :value="check.count !== 50" type="warning">
{{ check.count }} cards detected! (Decks should have exactly 50 cards) {{ check.count }} cards detected! (Decks should have exactly 50 cards)
@ -10,26 +10,33 @@
</v-alert> </v-alert>
<v-textarea <v-textarea
ref="deckList"
label="Edit Deck" label="Edit Deck"
v-model="new_decklist"
rows="35" rows="35"
hint="Change card counts and/or serial numbers. Names will be updated accordingly!" hint="Change card counts and/or serial numbers. Names will be updated accordingly!"
style="font-family: monospace" style="font-family: monospace"
:value="new_deck.deckList()"
@input="check.checked = false" @input="check.checked = false"
> >
</v-textarea> </v-textarea>
<v-card-actions> <v-card-actions>
<slot></slot> <v-btn color="error" @click.native="close">
<v-icon>cancel</v-icon>
cancel
</v-btn>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn color="info" @click.native="validate" :disabled="check.checked"> <v-btn color="info" @click.native="check_deck">
<v-icon>check</v-icon> <v-icon>check</v-icon>
validate validate
</v-btn> </v-btn>
<v-btn color="success" @click.native="save" :disabled="!check.checked"> <v-btn
color="success"
@click.native="save_deck"
:disabled="!check.checked"
>
<v-icon>save</v-icon> <v-icon>save</v-icon>
save save
</v-btn> </v-btn>
@ -45,51 +52,57 @@ export default {
name: 'DeckEditor', name: 'DeckEditor',
props: { props: {
deck: Object deck: Object,
visible: Boolean
}, },
data: () => ({ data: () => ({
check: null, check: {
new_deck: null count: 50,
maximum: 0,
checked: false
},
new_decklist: null
}), }),
created() { created() {
this.clear() this.new_decklist = this.deck.deckList()
},
computed: {
new_deck() {
let deck = new Deck()
deck.from_deckList(this.new_decklist)
deck.populate()
this.new_decklist = deck.deckList()
return deck
}
}, },
methods: { methods: {
clear() { close() {
this.check = { this.check.checked = false
count: 50, this.$emit('close')
maximum: 0,
checked: false
}
this.new_deck = new Deck(0)
if (this.deck)
// this.deck should already be populated!
this.new_deck.from_object(this.deck)
}, },
validate() { check_deck() {
this.new_deck.from_deckList(this.$refs.deckList.lazyValue)
this.new_deck.populate()
// count number of cards // count number of cards
this.check.count = this.new_deck.count() this.check.count = this.new_deck.count()
// find most frequent card // find most frequent card
this.check.maximum = 0 this.check.maximum = 0
for (let card of this.new_deck.cards) { this.new_deck.cards.forEach(card => {
if (card.count > this.check.maximum) this.check.maximum = card.count if (card.count > this.check.maximum) this.check.maximum = card.count
} })
// deck has now been checked // deck has now been checked
this.check.checked = true this.check.checked = true
}, },
save() { save_deck() {
this.$emit('save', this.new_deck) this.$emit('change', this.new_deck.plainObject())
this.close()
} }
} }
} }