works for v2

This commit is contained in:
2023-04-23 14:09:20 +02:00
parent ec2de227dd
commit 363e0a3b08
11 changed files with 462 additions and 307 deletions
+14
View File
@@ -15,6 +15,20 @@
<router-view />
</template>
<script lang="ts">
import { Player, Weapon, Server, useKillStore } from '@/stores/kill'
import { defineComponent } from 'vue'
export default defineComponent({
setup (props, ctx) {
const store = useKillStore()
store.fetchPlayers({})
store.fetchServers({})
store.fetchWeapons({})
}
})
</script>
<style>
:root {
--bg-color: #282a36;
+66 -59
View File
@@ -1,5 +1,6 @@
<template>
<div class="playerChart" ref="container">
<!-- <Scatter :data="chart" :options="chartOptions" /> -->
<Scatter :data="chart" :options="chartOptions" />
</div>
</template>
@@ -11,24 +12,34 @@ import {
PointElement,
LineElement,
Tooltip,
Legend
Legend,
CoreChartOptions,
PluginChartOptions,
ScaleChartOptions
} from 'chart.js'
import annotationPlugin from 'chartjs-plugin-annotation'
import dataLabel from 'chartjs-plugin-datalabels'
import { Store, useStore } from 'vuex'
import { Player, Weapon } from '@/store/index'
import { useKillStore, Player, Filters } from '@/stores/kill'
import { Scatter } from 'vue-chartjs'
import { defineComponent } from 'vue'
import { defineComponent, PropType } from 'vue'
import { ChartEvent } from 'chart.js/dist/core/core.plugins'
import { _DeepPartialObject } from 'chart.js/dist/types/utils'
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
export default defineComponent({
name: 'PlayerChart',
props: {
filters: Object,
filters: Object as PropType<Filters>,
playerHighlighted: String
},
data: () => {
return {
store: useKillStore(),
refreshColors: 0
}
},
emits: ['highlightPlayer'],
components: {
Scatter
@@ -54,7 +65,7 @@ export default defineComponent({
return colors
},
chart () {
const chartData = {
return {
datasets: [
{
label: 'Players',
@@ -62,29 +73,33 @@ export default defineComponent({
borderColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
pointRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 20 : 1,
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'dot',
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'circle',
hoverRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 30 : 4,
data: [] as { x: number, y: number }[]
data: this.playerIdList.map((id) => ({ y: this.players[id].kills, x: this.players[id].deaths }))
}
]
}
if (!this.players) {
return chartData
}
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 }
})
return chartData
},
chartOptions () {
chartOptions (): _DeepPartialObject<CoreChartOptions<'scatter'> & PluginChartOptions<'scatter'> & ScaleChartOptions<'scatter'>> {
// eslint-disable-next-line no-unused-expressions
this.$props.playerHighlighted
const options = {
let endX = 0
let endY = 0
if (this.players) {
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
if (maxY > maxX) {
endX = maxX
endY = (maxX / maxY) * maxY
} else {
endY = maxY
endX = (maxY / maxX) * maxX
}
}
return {
responsive: true,
maintainAspectRatio: true,
maintainAspectRatio: false,
animation: { duration: 500 },
layout: { autoPadding: false },
scales: {
@@ -124,7 +139,20 @@ export default defineComponent({
}
}
},
annotation: {},
annotation: {
annotations: {
line: {
type: 'line',
yMin: 0,
xMin: 0,
yMax: endY,
xMax: endX,
borderWidth: 2,
borderColor: this.colors.orange,
borderDash: [1, 5]
}
}
},
legend: { display: false },
datalabels: {
formatter: (value: any, context: any) => {
@@ -135,13 +163,13 @@ export default defineComponent({
borderWidth: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 1 : 0,
borderRadius: 5,
display: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 'true' : 'auto',
align: '-45',
align: -45,
anchor: 'end',
clamp: true,
color: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.bg : this.colors.fg
}
},
onClick: (e: Event, element: any) => {
onClick: (e: ChartEvent, element: any) => {
this.$emit('highlightPlayer', element.length > 0 ? this.playerIdList[element[0].index] : undefined)
},
onHover: (e: any, element: any) => {
@@ -149,44 +177,23 @@ export default defineComponent({
(e.native.target as HTMLElement).style.cursor = element[0] ? 'pointer' : 'default'
}
}
if (!this.players) {
return options
}
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
let endX, endY
if (maxY > maxX) {
endX = maxX
endY = (maxX / maxY) * maxY
} else {
endY = maxY
endX = (maxY / maxX) * maxX
}
options.plugins.annotation = {
annotations: {
line: {
type: 'line',
yMin: 0,
xMin: 0,
yMax: endY,
xMax: endX,
borderWidth: 2,
borderColor: this.colors.orange,
borderDash: [1, 5]
}
}
}
return options
},
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) || {} },
players (): { [key: string]: Player } {
const data = this.store.getPlayerList(this.filters || {})?.data
if (!data) return {}
const cut = Object.entries(data).sort((a, b) => {
if (a[1].kills < b[1].kills) {
return 1
}
if (a[1].kills > b[1].kills) {
return -1
}
return 0
}).slice(0, 200)
return Object.fromEntries(cut)
},
playerIdList (): string[] { return Object.keys(this.players) }
},
data () {
return {
store: useStore(),
refreshColors: 0
}
}
})
</script>
+25 -18
View File
@@ -7,10 +7,10 @@
<span v-on:click="sortPlayerList('kills')" :class="sortingData.argument == 'kills' ? 'selected' : ''">K</span>
<span v-on:click="sortPlayerList('deaths')" :class="sortingData.argument == 'deaths' ? 'selected' : ''">D</span>
<span v-on:click="sortPlayerList('k/d')" :class="sortingData.argument == 'k/d' ? 'selected' : ''">K/D</span>
<span v-on:click="sortPlayerList('max_kill_distance')"
:class="sortingData.argument == 'max_kill_distance' ? 'selected' : ''">max distance</span>
<span v-on:click="sortPlayerList('avg_kill_distance')"
:class="sortingData.argument == 'avg_kill_distance' ? 'selected' : ''">average distance</span>
<span v-on:click="sortPlayerList('max_distance')"
:class="sortingData.argument == 'max_distance' ? 'selected' : ''">max distance</span>
<span v-on:click="sortPlayerList('avg_distance')"
:class="sortingData.argument == 'avg_distance' ? 'selected' : ''">average distance</span>
</div>
<div :class="'playerRow ' + (playerId === $props.playerHighlighted ? 'selected' : '')"
v-for="(playerId, index) in playerIdList" v-bind:key="playerId" v-on:click="$emit('highlightPlayer', playerId)"
@@ -20,32 +20,34 @@
<div><span>{{ players[playerId].kills }}</span></div>
<div><span>{{ players[playerId].deaths }}</span></div>
<div><span>{{ Math.round(players[playerId].kills / Math.max(1, players[playerId].deaths) * 100) / 100 }}</span></div>
<div><span>{{ players[playerId].max_kill_distance }}</span></div>
<div><span>{{ Math.round(players[playerId].avg_kill_distance * 100) / 100 }}</span></div>
<div><span>{{ players[playerId].max_distance }}</span></div>
<div><span>{{ Math.round(((players[playerId].total_distance / players[playerId].kills) || 0) * 100) / 100 }}</span></div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { Player } from '@/store/index'
import { useStore } from 'vuex'
import { defineComponent, PropType } from 'vue'
import { useKillStore, Player, Filters } from '@/stores/kill'
export default defineComponent({
props: {
filters: Object,
filters: { type: Object as PropType<Filters>, default: () => ({}) },
playerHighlighted: String
},
emits: ['highlightPlayer'],
data () {
return {
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' },
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' | 'avg_distance' },
playerIdList: [] as string[],
store: useStore()
store: useKillStore()
}
},
computed: {
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) }
players (): { [key: string]: Player } {
const { player: _, ...withoutPlayer } = this.filters
return this.store.getPlayerList(withoutPlayer)?.data || {}
}
},
watch: {
players: {
@@ -55,12 +57,13 @@ export default defineComponent({
this.sortingData.direction *= -1
this.playerIdList = Object.keys(newval)
this.sortPlayerList(this.sortingData.argument)
this.playerIdList = this.playerIdList.slice(0, 500)
},
immediate: true
},
playerHighlighted (newval) {
const newElement = this.$refs['player:' + newval] as HTMLElement[]
newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
if (newElement && newElement[0]) { newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' }) }
}
},
updated () {
@@ -69,7 +72,7 @@ export default defineComponent({
element[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
},
methods: {
sortPlayerList (arg: ((keyof Player) | 'k/d')) {
sortPlayerList (arg: ((keyof Player) | 'k/d' | 'avg_distance')) {
if (arg === this.sortingData.argument) {
this.sortingData.direction *= -1
} else {
@@ -87,11 +90,14 @@ export default defineComponent({
if (arg === 'k/d') {
varA = this.players[a].kills / Math.max(1, this.players[a].deaths)
varB = this.players[b].kills / Math.max(1, this.players[b].deaths)
} else if (arg === 'avg_distance') {
varA = (this.players[a].total_distance / this.players[a].kills) || 0
varB = (this.players[b].total_distance / this.players[b].kills) || 0
} else {
varA = this.players[a][arg]
varB = this.players[b][arg]
}
if (varA === undefined || varB === undefined) return 0
if (varA < varB) {
return -1 * this.sortingData.direction
}
@@ -136,13 +142,14 @@ export default defineComponent({
margin-right: 1rem;
overflow: auto;
height: 100%;
user-select: none;
}
.playerRow,
.playerHeaders {
box-sizing: border-box;
display: grid;
grid-template-columns: 4ch 20ch 6ch 6ch 6ch 10ch 1fr;
grid-template-columns: 5ch 20ch 6ch 6ch 6ch 10ch 1fr;
background: var(--bg-color);
text-align: left;
cursor: pointer;
@@ -201,7 +208,7 @@ export default defineComponent({
.playerRow,
.playerHeaders {
grid-template-columns: 4ch 20ch 6ch 6ch 1fr;
grid-template-columns: 5ch 20ch 6ch 6ch 1fr;
}
}
+38 -33
View File
@@ -5,28 +5,18 @@
</template>
<script lang="ts">
import {
Chart as ChartJS,
ArcElement,
PointElement,
LineElement,
Tooltip,
Legend
} from 'chart.js'
import annotationPlugin from 'chartjs-plugin-annotation'
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
import dataLabel from 'chartjs-plugin-datalabels'
import { Store, useStore } from 'vuex'
import { Player, Weapon } from '@/store/index'
import { Weapon, Filters, useKillStore } from '@/stores/kill'
import { Doughnut } from 'vue-chartjs'
import { defineComponent } from 'vue'
import { functionExpression } from '@babel/types'
import { defineComponent, PropType } from 'vue'
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
export default defineComponent({
name: 'PlayerChart',
props: {
filters: Object,
filters: Object as PropType<Filters>,
playerHighlighted: String
},
emits: ['highlightPlayer'],
@@ -36,6 +26,12 @@ export default defineComponent({
mounted () {
this.refreshColors++
},
data: () => {
return {
store: useKillStore(),
refreshColors: 0
}
},
computed: {
colors () {
// eslint-disable-next-line no-unused-expressions
@@ -59,14 +55,26 @@ export default defineComponent({
return colors
},
chart () {
const colors = ['cyan', 'green', 'orange', 'pink', 'purple', 'red', 'yellow']
const colors = [
'cyan',
'green',
'orange',
'pink',
'purple',
'red',
'yellow'
]
const chartData = {
datasets: [
{
label: 'Weapons',
labels: this.sortedWeaponList || [] as string[],
borderColor: (context: any) => this.colors.fg,
backgroundColor: (context: any) => { return this.colors[colors[(context.dataIndex * 3) % colors.length]] },
labels: this.sortedWeaponList || ([] as string[]),
borderColor: () => this.colors.fg,
backgroundColor: (context: any) => {
return this.colors[
colors[(context.dataIndex * 3) % colors.length]
]
},
data: [] as number[]
}
]
@@ -74,7 +82,7 @@ export default defineComponent({
if (!this.sortedWeaponList) {
return chartData
}
chartData.datasets[0].data = this.sortedWeaponList.map(e => {
chartData.datasets[0].data = this.sortedWeaponList.map((e) => {
if (!this.weapons) {
return 0
}
@@ -103,10 +111,9 @@ export default defineComponent({
borderWidth: 2,
color: this.colors.bg,
display: (context: any) => {
return context.dataIndex === context.dataset.labels.length - 1 ? true : 'auto'
},
font: {
weight: 'bold'
return context.dataIndex === context.dataset.labels.length - 1
? true
: 'auto'
},
padding: 6
},
@@ -124,13 +131,18 @@ export default defineComponent({
}
}
}
}
}
return options
},
weapons (): { [key: string]: Weapon } {
return this.store.getters.getWeaponList(this.filters)
const { weapon: _, ...withoutWeapon } = this.filters || {}
const data = this.store.getWeaponList(withoutWeapon)?.data
if (!data) {
this.store.fetchWeapons(withoutWeapon)
return {}
}
return data
},
sortedWeaponList (): string[] {
if (!this.weapons) return []
@@ -146,13 +158,6 @@ export default defineComponent({
})
return weapons
}
},
data () {
return {
store: useStore(),
refreshColors: 0
}
}
})
</script>
@@ -165,7 +170,7 @@ export default defineComponent({
@media only screen and (max-width: 922px) {
canvas {
display: none !important
display: none !important;
}
}
</style>
+3 -5
View File
@@ -1,10 +1,8 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { createPinia } from 'pinia'
createApp(App).use(store).use(router).mount('#app')
const pinia = createPinia()
store.dispatch('fetchServers', {})
store.dispatch('fetchPlayers', {})
store.dispatch('fetchWeapons', {})
createApp(App).use(pinia).use(router).mount('#app')
-110
View File
@@ -1,110 +0,0 @@
// store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'
export interface Player {
deaths: number;
username: string;
// eslint-disable-next-line camelcase
max_kill_distance: number;
// eslint-disable-next-line camelcase
avg_kill_distance: number;
kills: number;
}
export interface Weapon {
id?:string,
// 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
export interface State {
servers: Server[]
players: { [key: string]: { [key: string]: Player } };
weapons: { [key: string]:{ [key: string]: Weapon }};
}
// define injection key
// eslint-disable-next-line symbol-description
export const key: InjectionKey<Store<State>> = Symbol()
export default createStore<State>({
state: {
servers: [],
players: {},
weapons: {}
},
getters: {
getPlayerList: (state) => ({ server, weapon, minKills, minDeaths }: { server?: number, weapon?: string, minKills:number, minDeaths:number }) => {
let playerFilter: string[] = []
if (state.players['']) {
playerFilter = Object.entries(state.players['']).filter(([, value]:any) => { return (value.kills >= minKills) || (value.deaths >= minDeaths) }).map(e => e[0])
}
if (playerFilter.length > 0 && state.players[(server || '') + (weapon || '')]) {
return Object.fromEntries(Object.entries(state.players[(server || '') + (weapon || '')]).filter(([key]) => playerFilter.includes(key)))
}
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)
}
},
modules: {}
})
+113
View File
@@ -0,0 +1,113 @@
import { defineStore } from 'pinia'
import * as assert from 'assert'
function removeNullEntries (a:any) {
return Object.fromEntries(Object.entries(a).filter(e => e[1] !== undefined && e[1] !== null))
}
function objectEqual (a:any, b:any) {
try {
a = removeNullEntries(a)
b = removeNullEntries(b)
assert.deepStrictEqual(a, b)
return true
} catch {
return false
}
}
export interface Filters {
server?: string;
player?: string;
weapon?: string;
host?: string;
map?: string;
gamemode?: string;
}
export interface Kill {
deaths: number;
kills: number;
// eslint-disable-next-line camelcase
max_distance: number;
// eslint-disable-next-line camelcase
total_distance: number;
}
export interface KillData<T extends Kill> {
filter: Filters;
data: { [key: string]: T };
}
export interface Player extends Kill {
username: string;
// eslint-disable-next-line camelcase
deaths_while_equipped?: number;
}
export interface Weapon extends Kill {
// eslint-disable-next-line camelcase
deaths_while_equipped: number;
}
export interface Server extends Kill {
host: number;
}
// define your typings for the store state
export interface State {
servers: KillData<Server>[];
players: KillData<Player>[];
weapons: KillData<Weapon>[];
maps: KillData<Kill>[];
gamemodes: KillData<Kill>[];
hosts: { [key: number]: string };
}
export const useKillStore = defineStore('kill', {
state: (): State => ({
servers: [],
players: [],
weapons: [],
maps: [],
gamemodes: [],
hosts: {}
}),
getters: {
getPlayerList: (state) => (filters: Filters) => state.players.find((e) => objectEqual(e.filter, filters)),
getWeaponList: (state) => (filters: Filters) => state.weapons.find((e) => objectEqual(e.filter, filters)),
getServerList: (state) => (filters: Filters) => state.servers.find((e) => objectEqual(e.filter, filters))
},
actions: {
async fetchPlayers (filter: Filters) {
filter = removeNullEntries(filter)
const response = await fetch(
'https://tone.sleepycat.date/v2_test/client/players?' +
new URLSearchParams(filter as Record<keyof Filters, string>)
)
const data = await response.json()
const entry = this.players.find((e) => objectEqual(e.filter, filter))
if (!entry) return this.players.push({ filter, data })
entry.data = data
},
async fetchWeapons (filter: Filters) {
filter = removeNullEntries(filter)
const response = await fetch(
'https://tone.sleepycat.date/v2_test/client/weapons?' +
new URLSearchParams(filter as Record<keyof Filters, string>)
)
const data = await response.json()
const entry = this.weapons.find((e) => objectEqual(e.filter, filter))
if (!entry) return this.weapons.push({ filter, data })
entry.data = data
},
async fetchServers (filter: Filters) {
filter = removeNullEntries(filter)
const response = await fetch(
'https://tone.sleepycat.date/v2_test/client/servers?' +
new URLSearchParams(filter as Record<keyof Filters, string>)
)
const data = await response.json()
const entry = this.servers.find((e) => objectEqual(e.filter, filter))
if (!entry) return this.servers.push({ filter, data })
entry.data = data
}
}
})
+44 -42
View File
@@ -12,16 +12,16 @@
<option v-for="weaponId in sortedWeaponList" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
</select> -->
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select server" v-model="models.server"
:options="servers" :allow-empty="true" :custom-label="((e) => e.name)" @select="changeFilter({ server: $event.id })"
@remove="changeFilter({ server: '' })"></VueMultiselect>
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select server" v-model="filters.server"
:options="sortedServerList" :allow-empty="true" :custom-label="((e:Server) => e)"
></VueMultiselect>
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select weapon" v-model="models.weapon"
:options="sortedWeaponList" :allow-empty="true" @select="changeFilter({ weapon: $event })"
@remove="changeFilter({ weapon: '' })"></VueMultiselect>
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select weapon" v-model="filters.weapon"
:options="sortedWeaponList" :allow-empty="true"
></VueMultiselect>
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Search player" v-model="playerHighlighted"
:options="sortedPlayerList" :allow-empty="true" :custom-label="((e) => players[e]?.username)"></VueMultiselect>
:options="sortedPlayerList" :allow-empty="true" :custom-label="((e:string) => players[e]?.username)"></VueMultiselect>
</div>
<div id="playerView">
@@ -34,8 +34,7 @@
</template>
<script lang="ts">
import { Store, useStore } from 'vuex'
import { Player, Weapon, Server } from '@/store/index'
import { Player, Weapon, Server, useKillStore, Filters } from '@/stores/kill'
import PlayerList from '@/components/PlayerList.vue'
import PlayerChart from '@/components/PlayerChart.vue'
import WeaponChart from '@/components/WeaponChart.vue'
@@ -46,26 +45,54 @@ import 'vue-multiselect/dist/vue-multiselect.css'
export default defineComponent({
name: 'PlayerView',
components: {
PlayerList, PlayerChart, VueMultiselect, WeaponChart
VueMultiselect, PlayerList, PlayerChart, WeaponChart
},
data () {
return {
models: {},
filters: { minKills: 100, minDeaths: 100 },
store: useStore(),
filters: {} as Filters,
store: useKillStore(),
playerHighlighted: undefined
} as { filters: { weapon?: string, server?: string }, store: Store<any>, playerHighlighted?: string }
}
},
computed: {
servers (): Server[] { return this.store.state.servers },
weapons (): { [key: string]: Weapon } { return this.store.getters.getWeaponList(this.filters) },
servers (): { [key: string]: Server } {
const { server: _, player: _1, ...withoutServer } = this.filters
const data = this.store.getServerList(withoutServer)?.data
if (!data) {
this.store.fetchServers(withoutServer)
return {}
}
return data
},
weapons (): { [key: string]: Weapon } {
const { weapon: _, player: _1, ...withoutWeapons } = this.filters
const data = this.store.getWeaponList(withoutWeapons)?.data
if (!data) {
this.store.fetchWeapons(withoutWeapons)
return {}
}
return data
},
players (): { [key: string]: Player } {
const { player: _, ...withoutPlayers } = this.filters
const data = this.store.getPlayerList(withoutPlayers)?.data
if (!data) {
this.store.fetchPlayers(withoutPlayers)
return {}
}
return data
},
sortedWeaponList (): string[] {
if (!this.weapons) return []
const weapons = Object.keys(this.weapons)
return weapons.sort()
},
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) },
sortedServerList ():string[] {
if (!this.servers) return []
const servers = Object.keys(this.servers)
return servers.sort()
},
sortedPlayerList (): string[] {
if (!this.players) return []
const players = Object.keys(this.players)
@@ -73,7 +100,6 @@ export default defineComponent({
if (!this.players) {
return 0
}
if (this.players[a].username < this.players[b].username) {
return -1
}
@@ -83,30 +109,6 @@ export default defineComponent({
return 0
})
}
},
watch: {
playerHighlighted: function (newval:string) {
this.fetchPlayerData({ player: newval, ...this.filters })
}
},
methods: {
async fetchPlayerData ({ player, server }: { player?: string, server?:string }) {
if (!this.store.getters.getWeaponList({ player, server })) return await this.store.dispatch('fetchWeapons', { player, server })
},
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))
promises.push(this.fetchPlayerData({ player: this.playerHighlighted, ...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
}
}
})
</script>