Change data loading to centralized store
This commit is contained in:
@@ -14,6 +14,8 @@ import {
|
|||||||
Legend
|
Legend
|
||||||
} from 'chart.js'
|
} from 'chart.js'
|
||||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||||
|
import { Store, useStore } from 'vuex'
|
||||||
|
import { Player, Weapon } from '@/store/index'
|
||||||
|
|
||||||
import { Scatter } from 'vue-chartjs'
|
import { Scatter } from 'vue-chartjs'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
@@ -23,7 +25,7 @@ ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annota
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PlayerChart',
|
name: 'PlayerChart',
|
||||||
props: {
|
props: {
|
||||||
players: Object
|
filters: Object
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Scatter
|
Scatter
|
||||||
@@ -37,15 +39,24 @@ export default defineComponent({
|
|||||||
labels: [] as string[],
|
labels: [] as string[],
|
||||||
borderColor: '#f87979',
|
borderColor: '#f87979',
|
||||||
backgroundColor: '#f87979',
|
backgroundColor: '#f87979',
|
||||||
|
pointRadius: [1],
|
||||||
|
pointStyle: ['dot'],
|
||||||
data: [] as {x:number, y:number}[]
|
data: [] as {x:number, y:number}[]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
if (!this.$props.players) {
|
if (!this.players) {
|
||||||
return chartData
|
return chartData
|
||||||
}
|
}
|
||||||
chartData.datasets[0].labels = Object.keys(this.$props.players)
|
chartData.datasets[0].labels = this.playerIdList
|
||||||
chartData.datasets[0].data = Object.values(this.$props.players).map((e:any) => ({ x: e.deaths, y: e.kills }))
|
chartData.datasets[0].data = this.playerIdList.map(e => {
|
||||||
|
if (!this.players) {
|
||||||
|
return { x: 0, y: 0 }
|
||||||
|
}
|
||||||
|
return { x: this.players[e].deaths, y: this.players[e].kills }
|
||||||
|
})
|
||||||
|
chartData.datasets[0].pointRadius = [1].fill(1, 0, chartData.datasets[0].labels.length)
|
||||||
|
chartData.datasets[0].pointStyle = ['dot'].fill('dot', 0, chartData.datasets[0].labels.length)
|
||||||
return chartData
|
return chartData
|
||||||
},
|
},
|
||||||
chartOptions () {
|
chartOptions () {
|
||||||
@@ -56,10 +67,10 @@ export default defineComponent({
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
label: (ctx:any) => {
|
label: (ctx:any) => {
|
||||||
let label:string
|
let label:string
|
||||||
if (!this.$props.players) {
|
if (!this.players) {
|
||||||
label = ctx.dataset.labels[ctx.dataIndex]
|
label = ctx.dataset.labels[ctx.dataIndex]
|
||||||
} else {
|
} else {
|
||||||
label = this.$props.players[ctx.dataset.labels[ctx.dataIndex]].username
|
label = this.players[ctx.dataset.labels[ctx.dataIndex]].username
|
||||||
}
|
}
|
||||||
label += ' (' + ctx.parsed.y + ' kills ' + ctx.parsed.x + ' deaths)'
|
label += ' (' + ctx.parsed.y + ' kills ' + ctx.parsed.x + ' deaths)'
|
||||||
return label
|
return label
|
||||||
@@ -69,11 +80,11 @@ export default defineComponent({
|
|||||||
annotation: {}
|
annotation: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.$props.players) {
|
if (!this.players) {
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
const maxX = Math.max(...Object.values(this.$props.players).map(e => e.deaths))
|
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
|
||||||
const maxY = Math.max(...Object.values(this.$props.players).map(e => e.kills))
|
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
|
||||||
let endX, endY
|
let endX, endY
|
||||||
if (maxY > maxX) {
|
if (maxY > maxX) {
|
||||||
endX = maxX
|
endX = maxX
|
||||||
@@ -97,16 +108,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
}
|
},
|
||||||
},
|
players ():{[key:string]:Player} { return this.store.getters.getPlayerList(this.filters) },
|
||||||
/* watch: {
|
playerIdList ():string[] { return Object.keys(this.players) }
|
||||||
players: function (newval: any, oldval: any): void {
|
|
||||||
|
|
||||||
}
|
},
|
||||||
}, */
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
store: useStore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<span v-on:click="sortPlayerList('avg_kill_distance')">average kill distance</span>
|
<span v-on:click="sortPlayerList('avg_kill_distance')">average kill distance</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="player" v-for="playerId in playerIdList" v-bind:key="playerId">
|
<div class="player" v-for="playerId in playerIdList" v-bind:key="playerId">
|
||||||
<span>{{ $props.players[playerId].username }}</span>
|
<span>{{ players[playerId].username }}</span>
|
||||||
<span>{{ players[playerId].kills }}</span>
|
<span>{{ players[playerId].kills }}</span>
|
||||||
<span>{{ players[playerId].deaths }}</span>
|
<span>{{ players[playerId].deaths }}</span>
|
||||||
<span>{{ Math.round(players[playerId].kills / players[playerId].deaths * 100) / 100 }}</span>
|
<span>{{ Math.round(players[playerId].kills / players[playerId].deaths * 100) / 100 }}</span>
|
||||||
@@ -21,27 +21,37 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
import { Player } from '@/store/index'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
players: Object
|
filters: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
sortingData: { direction: -1, argument: '' },
|
sortingData: { direction: -1, argument: '' as keyof Player | 'k/d' },
|
||||||
playerIdList: [] as string[]
|
playerIdList: [] as string[],
|
||||||
|
store: useStore()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
players ():{[key:string]:Player} { return this.store.getters.getPlayerList(this.filters) }
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
players: function (newval: any, oldval: any): void {
|
players: {
|
||||||
this.playerIdList = []
|
handler (newval:{[key:string]:Player}): void {
|
||||||
this.sortingData.direction *= -1
|
if (!newval) return
|
||||||
this.playerIdList = Object.keys(newval)
|
this.playerIdList = []
|
||||||
this.sortPlayerList(this.sortingData.argument)
|
this.sortingData.direction *= -1
|
||||||
|
this.playerIdList = Object.keys(newval)
|
||||||
|
this.sortPlayerList(this.sortingData.argument)
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
sortPlayerList (arg: string) {
|
sortPlayerList (arg: ((keyof Player) | 'k/d')) {
|
||||||
if (arg === this.sortingData.argument) {
|
if (arg === this.sortingData.argument) {
|
||||||
this.sortingData.direction *= -1
|
this.sortingData.direction *= -1
|
||||||
} else {
|
} else {
|
||||||
@@ -51,15 +61,17 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
this.sortingData.argument = arg
|
this.sortingData.argument = arg
|
||||||
|
|
||||||
this.playerIdList.sort((a:any, b:any) => {
|
this.playerIdList.sort((a:string, b:string) => {
|
||||||
if (!this.$props.players) {
|
if (!this.players) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
let varA = this.$props.players[a][arg]
|
let varA, varB
|
||||||
let varB = this.$props.players[b][arg]
|
|
||||||
if (arg === 'k/d') {
|
if (arg === 'k/d') {
|
||||||
varA = this.$props.players[a].kills / this.$props.players[a].deaths
|
varA = this.players[a].kills / this.players[a].deaths
|
||||||
varB = this.$props.players[b].kills / this.$props.players[b].deaths
|
varB = this.players[b].kills / this.players[b].deaths
|
||||||
|
} else {
|
||||||
|
varA = this.players[a][arg]
|
||||||
|
varB = this.players[b][arg]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (varA < varB) {
|
if (varA < varB) {
|
||||||
@@ -68,7 +80,6 @@ export default defineComponent({
|
|||||||
if (varA > varB) {
|
if (varA > varB) {
|
||||||
return 1 * this.sortingData.direction
|
return 1 * this.sortingData.direction
|
||||||
}
|
}
|
||||||
// names must be equal
|
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,3 +4,7 @@ import router from './router'
|
|||||||
import store from './store'
|
import store from './store'
|
||||||
|
|
||||||
createApp(App).use(store).use(router).mount('#app')
|
createApp(App).use(store).use(router).mount('#app')
|
||||||
|
|
||||||
|
store.dispatch('fetchServers', {})
|
||||||
|
store.dispatch('fetchPlayers', {})
|
||||||
|
store.dispatch('fetchWeapons', {})
|
||||||
|
|||||||
+74
-9
@@ -2,34 +2,99 @@
|
|||||||
import { InjectionKey } from 'vue'
|
import { InjectionKey } from 'vue'
|
||||||
import { createStore, Store } from 'vuex'
|
import { createStore, Store } from 'vuex'
|
||||||
|
|
||||||
interface Player {
|
export interface Player {
|
||||||
deaths: number;
|
deaths: number;
|
||||||
username: string;
|
username: string;
|
||||||
maxKillDistance: number;
|
// eslint-disable-next-line camelcase
|
||||||
avgKillDistance: number;
|
max_kill_distance: number;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
avg_kill_distance: number;
|
||||||
kills: number;
|
kills: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Weapon {
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
max_kill_distance: number;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
avg_kill_distance: number;
|
||||||
|
kills: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Server{
|
||||||
|
name:string;
|
||||||
|
id:number;
|
||||||
|
description:string
|
||||||
|
}
|
||||||
// define your typings for the store state
|
// define your typings for the store state
|
||||||
export interface State {
|
export interface State {
|
||||||
players: Player[];
|
servers: Server[]
|
||||||
|
players: { [key: string]: { [key: string]: Player } };
|
||||||
|
weapons: { [key: string]:{ [key: string]: Weapon }};
|
||||||
}
|
}
|
||||||
|
|
||||||
// define injection key
|
// define injection key
|
||||||
// eslint-disable-next-line symbol-description
|
// eslint-disable-next-line symbol-description
|
||||||
export const key: InjectionKey<Store<State>> = Symbol()
|
export const key: InjectionKey<Store<State>> = Symbol()
|
||||||
|
|
||||||
export default createStore({
|
export default createStore<State>({
|
||||||
state: {
|
state: {
|
||||||
servers: [],
|
servers: [],
|
||||||
players: {},
|
players: {},
|
||||||
weapons: {}
|
weapons: {}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getPlayerList (weapon, server) {
|
getPlayerList: (state) => ({ server, weapon }: { server?: number, weapon?: string }) => {
|
||||||
console.log('e')
|
return state.players[(server || '') + (weapon || '')]
|
||||||
|
},
|
||||||
|
getWeaponList: (state) => ({ server, player }: { server?: number, player?: string }) => {
|
||||||
|
return state.weapons[(server || '') + (player || '')]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setPlayers (state, data: { data: { [key: string]: Player }, filters: { weapon: string, server: number } }) {
|
||||||
|
state.players[(data.filters.server || '') + (data.filters.weapon || '')] = data.data
|
||||||
|
},
|
||||||
|
setWeapons (state, data:{data: {[key:string]:Weapon}, filters:{server:string, player:string}}) {
|
||||||
|
state.weapons[(data.filters.server || '') + (data.filters.player || '')] = data.data
|
||||||
|
},
|
||||||
|
setServers (state, data) {
|
||||||
|
state.servers = data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async fetchPlayers (context, { weapon, server }) {
|
||||||
|
const searchParams: Record<string, string> = {}
|
||||||
|
if (weapon?.toString()) { searchParams.weapon = weapon }
|
||||||
|
if (server?.toString()) { searchParams.server = server }
|
||||||
|
const response = await fetch(
|
||||||
|
'https://tone.sleepycat.date/v1/client/players?' + new URLSearchParams(searchParams)
|
||||||
|
)
|
||||||
|
const data = await response.json()
|
||||||
|
Object.keys(data).forEach(e => {
|
||||||
|
data[e].kills = Number(data[e].kills)
|
||||||
|
data[e].deaths = Number(data[e].deaths)
|
||||||
|
data[e].max_kill_distance = Number(data[e].max_kill_distance)
|
||||||
|
data[e].avg_kill_distance = Number(data[e].avg_kill_distance)
|
||||||
|
})
|
||||||
|
context.commit('setPlayers', { data, filters: { weapon, server } })
|
||||||
|
},
|
||||||
|
async fetchWeapons (context, { player, server }) {
|
||||||
|
const searchParams: Record<string, string> = {}
|
||||||
|
if (player?.toString()) { searchParams.player = player }
|
||||||
|
if (server?.toString()) { searchParams.server = server }
|
||||||
|
const response = await fetch(
|
||||||
|
'https://tone.sleepycat.date/v1/client/weapons?' + new URLSearchParams(searchParams)
|
||||||
|
)
|
||||||
|
const data = await response.json()
|
||||||
|
context.commit('setWeapons', { data, filters: { player, server } })
|
||||||
|
},
|
||||||
|
async fetchServers (context) {
|
||||||
|
const response = await fetch(
|
||||||
|
'https://tone.sleepycat.date/v1/client/servers'
|
||||||
|
)
|
||||||
|
const data = await response.json()
|
||||||
|
context.commit('setServers', data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations: {},
|
|
||||||
actions: {},
|
|
||||||
modules: {}
|
modules: {}
|
||||||
})
|
})
|
||||||
|
|||||||
+37
-59
@@ -1,82 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-model="filters.server" v-on:change="fetchPlayers(filters)">
|
<select v-on:change="changeFilter({ server: ($event.target as HTMLInputElement).value })">
|
||||||
<option></option>
|
<option></option>
|
||||||
<option v-for="(serverData, serverId) in servers" v-bind:key="serverId" :value="serverId">{{ serverData.name }}</option>
|
<option v-for="(serverData) in servers" v-bind:key="serverData.id" :value="serverData.id">{{ serverData.name }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<select v-model="filters.weapon" v-on:change="fetchPlayers(filters)">
|
<select v-on:change="changeFilter({ weapon: ($event.target as HTMLInputElement).value })">
|
||||||
<option></option>
|
<option></option>
|
||||||
<option v-for="(weaponData, weaponId) in weapons" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
|
<option v-for="(weaponData, weaponId) in weapons" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="playersData">
|
<div class="playersData">
|
||||||
|
|
||||||
<PlayerList :players="players"></PlayerList>
|
<PlayerList :filters="filters"></PlayerList>
|
||||||
<PlayerChart :players="players"></PlayerChart>
|
<PlayerChart :filters="filters"></PlayerChart>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Options, Vue } from 'vue-class-component'
|
import { Store, useStore } from 'vuex'
|
||||||
|
import { Player, Weapon, Server } from '@/store/index'
|
||||||
import PlayerList from '@/components/PlayerList.vue'
|
import PlayerList from '@/components/PlayerList.vue'
|
||||||
import PlayerChart from '@/components/PlayerChart.vue'
|
import PlayerChart from '@/components/PlayerChart.vue'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
@Options({
|
export default defineComponent({
|
||||||
|
name: 'PlayerView',
|
||||||
components: {
|
components: {
|
||||||
PlayerList, PlayerChart
|
PlayerList, PlayerChart
|
||||||
}
|
},
|
||||||
})
|
|
||||||
export default class PlayerView extends Vue {
|
|
||||||
players: any = {}
|
|
||||||
weapons: any = {}
|
|
||||||
servers: any = {}
|
|
||||||
filters: any = {}
|
|
||||||
created () {
|
|
||||||
this.fetchPlayers({})
|
|
||||||
this.fetchWeapons({})
|
|
||||||
this.fetchServers()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchWeapons ({ server }: { server?: string }) {
|
|
||||||
const searchParams: Record<string, string> = {}
|
|
||||||
if (server) { searchParams.server = server }
|
|
||||||
const response = await fetch(
|
|
||||||
'https://tone.sleepycat.date/v1/client/weapons?' + new URLSearchParams(searchParams)
|
|
||||||
)
|
|
||||||
const data = await response.json()
|
|
||||||
this.weapons = data
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchServers () {
|
|
||||||
const response = await fetch(
|
|
||||||
'https://tone.sleepycat.date/v1/client/servers'
|
|
||||||
)
|
|
||||||
const data = await response.json()
|
|
||||||
this.servers = data
|
|
||||||
console.log(this.servers)
|
|
||||||
}
|
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return { a: 1 }
|
return {
|
||||||
|
filters: {},
|
||||||
|
store: useStore()
|
||||||
|
} as { filters: { weapon?: string, server?: string }, store: Store<any> }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
servers (): Server[] { return this.store.state.servers },
|
||||||
|
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) },
|
||||||
|
weapons (): { [key: string]: Weapon } { return this.store.getters.getWeaponList(this.filters) }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async changeFilter ({ weapon, server }: { weapon?: string, server?: string }) {
|
||||||
|
const filters = JSON.parse(JSON.stringify(this.filters))
|
||||||
|
if (weapon !== undefined) filters.weapon = weapon
|
||||||
|
if (server !== undefined) filters.server = server
|
||||||
|
if (weapon === '') delete filters.weapon
|
||||||
|
if (server === '') delete filters.server
|
||||||
|
const promises = []
|
||||||
|
if (!this.store.getters.getPlayerList(filters)) promises.push(this.store.dispatch('fetchPlayers', filters))
|
||||||
|
if (!this.store.getters.getWeaponList(filters)) promises.push(this.store.dispatch('fetchWeapons', filters))
|
||||||
|
await Promise.all(promises)
|
||||||
|
// Delay the update of filters propery after we fetch the data to the API as changing it will cause subcomponents to reload before data is fetched
|
||||||
|
this.filters = filters
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
async fetchPlayers ({ weapon, server }: { weapon?: string, server?: string }) {
|
|
||||||
const searchParams: Record<string, string> = {}
|
|
||||||
if (weapon?.toString()) { searchParams.weapon = weapon }
|
|
||||||
if (server?.toString()) { searchParams.server = server }
|
|
||||||
console.log(server, weapon)
|
|
||||||
console.log('https://tone.sleepycat.date/v1/client/players?' + new URLSearchParams(searchParams))
|
|
||||||
const response = await fetch(
|
|
||||||
'https://tone.sleepycat.date/v1/client/players?' + new URLSearchParams(searchParams)
|
|
||||||
)
|
|
||||||
const data = await response.json()
|
|
||||||
Object.keys(data).forEach(e => {
|
|
||||||
data[e].kills = Number(data[e].kills)
|
|
||||||
data[e].deaths = Number(data[e].deaths)
|
|
||||||
data[e].max_kill_distance = Number(data[e].max_kill_distance)
|
|
||||||
data[e].avg_kill_distance = Number(data[e].avg_kill_distance)
|
|
||||||
})
|
|
||||||
this.players = data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user