add gamemode filter and multiselection
This commit is contained in:
@@ -3,3 +3,9 @@ indent_style = space
|
|||||||
indent_size = 2
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|||||||
+10
-1
@@ -1,21 +1,30 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
|
|
||||||
extends: [
|
extends: [
|
||||||
'plugin:vue/vue3-essential',
|
'plugin:vue/vue3-essential',
|
||||||
'@vue/standard',
|
'@vue/standard',
|
||||||
'@vue/typescript/recommended'
|
'@vue/typescript/recommended'
|
||||||
],
|
],
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2020
|
ecmaVersion: 2020
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'@typescript-eslint/indent': ['error', 2],
|
'@typescript-eslint/indent': ['error', 2],
|
||||||
camelcase: 0
|
camelcase: 0
|
||||||
}
|
},
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'@vue/standard',
|
||||||
|
'@vue/typescript/recommended'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-13463
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gamemodeChart" ref="container">
|
||||||
|
<LoadingBar v-if="progress !== 1" :value="progress"></LoadingBar>
|
||||||
|
<Doughnut :data="chart" :options="chartOptions" v-if="progress === 1" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Chart as ChartJS, ArcElement, Tooltip, Legend, ChartData, ChartOptions } from 'chart.js'
|
||||||
|
import dataLabel from 'chartjs-plugin-datalabels'
|
||||||
|
import { Weapon, Filter, useKillStore } from '@/stores/kill'
|
||||||
|
import { Doughnut } from 'vue-chartjs'
|
||||||
|
import { defineComponent, PropType, Ref, unref } from 'vue'
|
||||||
|
import gamemodes from '../stores/gamemodes.json'
|
||||||
|
|
||||||
|
import LoadingBar from './LoadingBar.vue'
|
||||||
|
|
||||||
|
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'GamemodeChart',
|
||||||
|
props: {
|
||||||
|
filters: Object as PropType<Filter>,
|
||||||
|
playerHighlighted: String
|
||||||
|
},
|
||||||
|
emits: ['highlightPlayer'],
|
||||||
|
components: {
|
||||||
|
Doughnut, LoadingBar
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.refreshColors++
|
||||||
|
},
|
||||||
|
data: () => {
|
||||||
|
return {
|
||||||
|
store: useKillStore(),
|
||||||
|
refreshColors: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
progress () {
|
||||||
|
const filter = new Filter(this.filters)
|
||||||
|
delete filter.gamemode
|
||||||
|
const data = this.store.getList('gamemodes', filter)?.value
|
||||||
|
if (!data) return this.store.fetch('gamemodes', filter).value.progress.value
|
||||||
|
return data.progress.value
|
||||||
|
},
|
||||||
|
colors () {
|
||||||
|
// eslint-disable-next-line no-unused-expressions
|
||||||
|
this.refreshColors
|
||||||
|
const colors = {} as { [key: string]: string }
|
||||||
|
if (this.$refs.container) {
|
||||||
|
const style = getComputedStyle(this.$refs.container as Element)
|
||||||
|
colors.fg = style.getPropertyValue('--foreground')
|
||||||
|
colors.bg = style.getPropertyValue('--bg-color')
|
||||||
|
colors.orange = style.getPropertyValue('--orange')
|
||||||
|
colors.cyan = style.getPropertyValue('--cyan')
|
||||||
|
colors.yellow = style.getPropertyValue('--yellow')
|
||||||
|
colors.green = style.getPropertyValue('--green')
|
||||||
|
colors.purple = style.getPropertyValue('--purple')
|
||||||
|
colors.red = style.getPropertyValue('--red')
|
||||||
|
colors.pink = style.getPropertyValue('--pink')
|
||||||
|
colors.currentLine = style.getPropertyValue('--current-line')
|
||||||
|
} else {
|
||||||
|
colors.fg = '#ffffff'
|
||||||
|
}
|
||||||
|
return colors
|
||||||
|
},
|
||||||
|
chart (): ChartData<'doughnut', number[]> {
|
||||||
|
const colors = [
|
||||||
|
'cyan',
|
||||||
|
'green',
|
||||||
|
'orange',
|
||||||
|
'pink',
|
||||||
|
'purple',
|
||||||
|
'red',
|
||||||
|
'yellow'
|
||||||
|
]
|
||||||
|
const chartData: ChartData<'doughnut', number[]> = {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Weapons',
|
||||||
|
borderColor: () => this.colors.fg,
|
||||||
|
backgroundColor: (context) => {
|
||||||
|
return this.colors[
|
||||||
|
colors[(context.dataIndex * 3) % colors.length]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
data: [] as number[]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (!this.sortedList) {
|
||||||
|
return chartData
|
||||||
|
}
|
||||||
|
chartData.datasets[0].data = this.sortedList.map((e) => {
|
||||||
|
if (!this.values) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return this.values[e].value.kills
|
||||||
|
})
|
||||||
|
return chartData
|
||||||
|
},
|
||||||
|
chartOptions (): ChartOptions<'doughnut'> {
|
||||||
|
// eslint-disable-next-line no-unused-expressions
|
||||||
|
this.$props.playerHighlighted
|
||||||
|
const options: ChartOptions<'doughnut'> = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: true,
|
||||||
|
animation: { duration: 500 },
|
||||||
|
layout: { autoPadding: false },
|
||||||
|
plugins: {
|
||||||
|
datalabels: {
|
||||||
|
formatter: (value, context) => {
|
||||||
|
const weaponId = this.sortedList[context.dataIndex]
|
||||||
|
// if (!(weapons as {[key:string]:string})[weaponId]) console.log(weaponId)
|
||||||
|
return (gamemodes as { [key: string]: string })[weaponId] || weaponId
|
||||||
|
},
|
||||||
|
backgroundColor: (context) => {
|
||||||
|
return context.dataset.backgroundColor(context, options)
|
||||||
|
},
|
||||||
|
borderColor: this.colors.fg,
|
||||||
|
borderRadius: 25,
|
||||||
|
borderWidth: 2,
|
||||||
|
color: this.colors.bg,
|
||||||
|
display: (context) => {
|
||||||
|
return context.dataIndex === context.dataset.data.length - 1
|
||||||
|
? true
|
||||||
|
: 'auto'
|
||||||
|
},
|
||||||
|
padding: 6
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: (ctx: any) => {
|
||||||
|
let label: string
|
||||||
|
if (!this.sortedList) {
|
||||||
|
label = ctx.dataset.labels[ctx.dataIndex]
|
||||||
|
} else {
|
||||||
|
label = this.sortedList[ctx.dataIndex]
|
||||||
|
}
|
||||||
|
label += ' (' + this.values[label].value.kills + ' kills)'
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
},
|
||||||
|
values (): { [key: string]: Ref<Weapon> } {
|
||||||
|
const filter = new Filter({ ...this.filters, player: this.playerHighlighted })
|
||||||
|
delete filter.gamemode
|
||||||
|
const data = this.store.getList('gamemodes', filter)?.value.data
|
||||||
|
if (!data) return this.store.fetch('gamemodes', filter).value.data
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
sortedList (): string[] {
|
||||||
|
if (!this.values) return []
|
||||||
|
const weapons = Object.keys(this.values).filter(e => unref(this.values[e]).kills > 0)
|
||||||
|
weapons.sort((a, b) => {
|
||||||
|
if (Number(this.values[a].value.kills) < Number(unref(this.values[b]).kills)) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (Number(this.values[a].value.kills) > Number(unref(this.values[b]).kills)) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
return weapons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.weaponChart {
|
||||||
|
width: calc(min(50vw, 50vh) - 3em);
|
||||||
|
height: calc(min(50vw, 50vh) - 3em);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 922px) {
|
||||||
|
canvas {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -66,11 +66,11 @@ export default defineComponent({
|
|||||||
} else if (mustScrollDown) {
|
} else if (mustScrollDown) {
|
||||||
this.vIndex = Math.min(scrollIndex, this.list.length - this.toLoad)
|
this.vIndex = Math.min(scrollIndex, this.list.length - this.toLoad)
|
||||||
}
|
}
|
||||||
if (mustScrollDown || mustScrollUp) {
|
// if (mustScrollDown || mustScrollUp) {
|
||||||
const msg = (mustScrollDown ? 'down ' : '') + (mustScrollUp ? 'up ' : '')
|
// const msg = (mustScrollDown ? 'down ' : '') + (mustScrollUp ? 'up ' : '')
|
||||||
// console.log(this.toLoad)
|
// console.log(this.toLoad)
|
||||||
// console.log((this.list.length * this.$props.rowHeight) - (this.vIndex * this.$props.rowHeight) - (this.visibleCount * this.$props.rowHeight))
|
// console.log((this.list.length * this.$props.rowHeight) - (this.vIndex * this.$props.rowHeight) - (this.visibleCount * this.$props.rowHeight))
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import dataLabel from 'chartjs-plugin-datalabels'
|
|||||||
import { useKillStore, Player, Filter } from '@/stores/kill'
|
import { useKillStore, Player, Filter } from '@/stores/kill'
|
||||||
|
|
||||||
import { Scatter } from 'vue-chartjs'
|
import { Scatter } from 'vue-chartjs'
|
||||||
import { defineComponent, PropType, Ref, toRaw, triggerRef, unref } from 'vue'
|
import { defineComponent, PropType, toRaw, unref } from 'vue'
|
||||||
import { ChartEvent } from 'chart.js/dist/core/core.plugins'
|
import { ChartEvent } from 'chart.js/dist/core/core.plugins'
|
||||||
import { _DeepPartialObject } from 'chart.js/dist/types/utils'
|
import { _DeepPartialObject } from 'chart.js/dist/types/utils'
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ export default defineComponent({
|
|||||||
if (this.filters) {
|
if (this.filters) {
|
||||||
const filter = new Filter(this.filters)
|
const filter = new Filter(this.filters)
|
||||||
delete filter.player
|
delete filter.player
|
||||||
return this.store.getList('players', filter)?.value.progress
|
return this.store.getList('players', filter)?.value.progress.value
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import { defineComponent, PropType, Ref, isRef } from 'vue'
|
|||||||
import { useKillStore, Player, Filter } from '@/stores/kill'
|
import { useKillStore, Player, Filter } from '@/stores/kill'
|
||||||
import VirtualList from './List/VirtualList.vue'
|
import VirtualList from './List/VirtualList.vue'
|
||||||
import LoadingBar from './LoadingBar.vue'
|
import LoadingBar from './LoadingBar.vue'
|
||||||
import { cloneProxy } from 'vue-chartjs/dist/utils'
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { VirtualList, LoadingBar },
|
components: { VirtualList, LoadingBar },
|
||||||
@@ -51,14 +50,13 @@ export default defineComponent({
|
|||||||
progress () {
|
progress () {
|
||||||
const filter = new Filter(this.filters)
|
const filter = new Filter(this.filters)
|
||||||
delete filter.player
|
delete filter.player
|
||||||
return this.store.getList('players', filter)?.value.progress
|
return this.store.getList('players', filter)?.value.progress.value
|
||||||
},
|
},
|
||||||
players (): { [key: string]: Ref<Player> } {
|
players (): { [key: string]: Ref<Player> } {
|
||||||
const filter = new Filter(this.filters)
|
const filter = new Filter(this.filters)
|
||||||
delete filter.player
|
delete filter.player
|
||||||
const data = this.store.getList('players', filter)?.value.data
|
const data = this.store.getList('players', filter)?.value.data
|
||||||
if (!data) return this.store.fetch('players', filter).value.data
|
if (!data) return this.store.fetch('players', filter).value.data
|
||||||
console.log(isRef(data), isRef(Object.values(data)[0]))
|
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
playerList (): (Ref<Player> & { id: string })[] {
|
playerList (): (Ref<Player> & { id: string })[] {
|
||||||
@@ -149,7 +147,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.playerTable {
|
.playerTable {
|
||||||
grid-area: list;
|
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
import { Chart as ChartJS, ArcElement, Tooltip, Legend, ChartData, ChartOptions } from 'chart.js'
|
||||||
import dataLabel from 'chartjs-plugin-datalabels'
|
import dataLabel from 'chartjs-plugin-datalabels'
|
||||||
import { Weapon, Filter, useKillStore } from '@/stores/kill'
|
import { Weapon, Filter, useKillStore } from '@/stores/kill'
|
||||||
import { Doughnut } from 'vue-chartjs'
|
import { Doughnut } from 'vue-chartjs'
|
||||||
@@ -41,8 +41,8 @@ export default defineComponent({
|
|||||||
const filter = new Filter(this.filters)
|
const filter = new Filter(this.filters)
|
||||||
delete filter.weapon
|
delete filter.weapon
|
||||||
const data = this.store.getList('weapons', filter)?.value
|
const data = this.store.getList('weapons', filter)?.value
|
||||||
if (!data) return this.store.fetch('weapons', filter).value.progress
|
if (!data) return this.store.fetch('weapons', filter).value.progress.value
|
||||||
return data.progress
|
return data.progress.value
|
||||||
},
|
},
|
||||||
colors () {
|
colors () {
|
||||||
// eslint-disable-next-line no-unused-expressions
|
// eslint-disable-next-line no-unused-expressions
|
||||||
@@ -65,7 +65,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return colors
|
return colors
|
||||||
},
|
},
|
||||||
chart () {
|
chart (): ChartData<'doughnut', number[]> {
|
||||||
const colors = [
|
const colors = [
|
||||||
'cyan',
|
'cyan',
|
||||||
'green',
|
'green',
|
||||||
@@ -75,13 +75,12 @@ export default defineComponent({
|
|||||||
'red',
|
'red',
|
||||||
'yellow'
|
'yellow'
|
||||||
]
|
]
|
||||||
const chartData = {
|
const chartData: ChartData<'doughnut', number[]> = {
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Weapons',
|
label: 'Weapons',
|
||||||
labels: this.sortedWeaponList || ([] as string[]),
|
|
||||||
borderColor: () => this.colors.fg,
|
borderColor: () => this.colors.fg,
|
||||||
backgroundColor: (context: any) => {
|
backgroundColor: (context) => {
|
||||||
return this.colors[
|
return this.colors[
|
||||||
colors[(context.dataIndex * 3) % colors.length]
|
colors[(context.dataIndex * 3) % colors.length]
|
||||||
]
|
]
|
||||||
@@ -101,30 +100,30 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
return chartData
|
return chartData
|
||||||
},
|
},
|
||||||
chartOptions () {
|
chartOptions (): ChartOptions<'doughnut'> {
|
||||||
// eslint-disable-next-line no-unused-expressions
|
// eslint-disable-next-line no-unused-expressions
|
||||||
this.$props.playerHighlighted
|
this.$props.playerHighlighted
|
||||||
const options = {
|
const options: ChartOptions<'doughnut'> = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: true,
|
maintainAspectRatio: true,
|
||||||
animation: { duration: 500 },
|
animation: { duration: 500 },
|
||||||
layout: { autoPadding: false },
|
layout: { autoPadding: false },
|
||||||
plugins: {
|
plugins: {
|
||||||
datalabels: {
|
datalabels: {
|
||||||
formatter: (value: any, context: any) => {
|
formatter: (value, context) => {
|
||||||
const weaponId = this.sortedWeaponList[context.dataIndex]
|
const weaponId = this.sortedWeaponList[context.dataIndex]
|
||||||
// if (!(weapons as {[key:string]:string})[weaponId]) console.log(weaponId)
|
// if (!(weapons as {[key:string]:string})[weaponId]) console.log(weaponId)
|
||||||
return (weapons as { [key: string]: string })[weaponId] || weaponId
|
return (weapons as { [key: string]: string })[weaponId] || weaponId
|
||||||
},
|
},
|
||||||
backgroundColor: (context: any) => {
|
backgroundColor: (context) => {
|
||||||
return context.dataset.backgroundColor(context)
|
return context.dataset.backgroundColor(context, options)
|
||||||
},
|
},
|
||||||
borderColor: this.colors.fg,
|
borderColor: this.colors.fg,
|
||||||
borderRadius: 25,
|
borderRadius: 25,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
color: this.colors.bg,
|
color: this.colors.bg,
|
||||||
display: (context: any) => {
|
display: (context) => {
|
||||||
return context.dataIndex === context.dataset.labels.length - 1
|
return context.dataIndex === context.dataset.data.length - 1
|
||||||
? true
|
? true
|
||||||
: 'auto'
|
: 'auto'
|
||||||
},
|
},
|
||||||
@@ -153,7 +152,6 @@ export default defineComponent({
|
|||||||
delete filter.weapon
|
delete filter.weapon
|
||||||
const data = this.store.getList('weapons', filter)?.value.data
|
const data = this.store.getList('weapons', filter)?.value.data
|
||||||
if (!data) return this.store.fetch('weapons', filter).value.data
|
if (!data) return this.store.fetch('weapons', filter).value.data
|
||||||
console.log(filter.toURLSearchParams().toString(), filter)
|
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
sortedWeaponList (): string[] {
|
sortedWeaponList (): string[] {
|
||||||
|
|||||||
+2
-2
@@ -52,8 +52,8 @@ socket.onmessage = function (e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function registerWebSocketKill (data : websocketData) {
|
function registerWebSocketKill (data : websocketData) {
|
||||||
const { player: _, ...filterWithoutPlayer } = store.$state.currentFilter
|
const filter = new Filter(store.$state.currentFilter)
|
||||||
const filter = new Filter(filterWithoutPlayer)
|
delete filter.player
|
||||||
const list = unref(store.getList('players', filter))
|
const list = unref(store.getList('players', filter))
|
||||||
if (!list) return
|
if (!list) return
|
||||||
if (!(!filter.server || filter.server.includes(data.servername))) return
|
if (!(!filter.server || filter.server.includes(data.servername))) return
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"private_match": "Private Match",
|
||||||
|
"aitdm": "Attrition",
|
||||||
|
"at": "Bounty Hunt",
|
||||||
|
"coliseum": "Coliseum",
|
||||||
|
"cp": "Amped Hardpoint",
|
||||||
|
"ctf": "Capture the Flag",
|
||||||
|
"fw": "Frontier War",
|
||||||
|
"lts": "Last Titan Standing",
|
||||||
|
"mfd": "Marked For Death",
|
||||||
|
"ps": "Pilots vs. Pilots",
|
||||||
|
"solo": "Campaign",
|
||||||
|
"tdm": "Skirmish",
|
||||||
|
"ttdm": "Titan Brawl",
|
||||||
|
"sns": "Stick and Stones",
|
||||||
|
"gg": "GunGame",
|
||||||
|
"fd": "Frontier Defense",
|
||||||
|
"ffa": "Free For All"
|
||||||
|
}
|
||||||
+11
-8
@@ -1,5 +1,5 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { Ref, ref, shallowRef, triggerRef, unref, isRef } from 'vue'
|
import { Ref, ref, shallowRef, triggerRef, unref } from 'vue'
|
||||||
|
|
||||||
export class Filter {
|
export class Filter {
|
||||||
constructor (filt?:{server?:string[], player?: string, weapon?: string[], host?: string[], map?: string[], gamemode?: string[]}) {
|
constructor (filt?:{server?:string[], player?: string, weapon?: string[], host?: string[], map?: string[], gamemode?: string[]}) {
|
||||||
@@ -53,7 +53,10 @@ function fetchWithLoading (url: string, progress: (percentage: number) => void)
|
|||||||
return new Response(
|
return new Response(
|
||||||
new ReadableStream({
|
new ReadableStream({
|
||||||
start (controller) {
|
start (controller) {
|
||||||
const reader = response.body!.getReader()
|
if (response.body === null) {
|
||||||
|
throw new Error('response.body is null')
|
||||||
|
}
|
||||||
|
const reader = response.body.getReader()
|
||||||
|
|
||||||
read()
|
read()
|
||||||
function read () {
|
function read () {
|
||||||
@@ -109,7 +112,7 @@ export interface Server extends Kill {
|
|||||||
|
|
||||||
export interface KillData<T extends Kill> {
|
export interface KillData<T extends Kill> {
|
||||||
data: { [key: string]: Ref<T> };
|
data: { [key: string]: Ref<T> };
|
||||||
progress?: number;
|
progress: Ref<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NSServer {
|
export interface NSServer {
|
||||||
@@ -173,7 +176,7 @@ export const useKillStore = defineStore('kill', {
|
|||||||
fetch <T extends StateProperty> (type:T, filter?:Filter): Ref<KillData<StateType<T>>> {
|
fetch <T extends StateProperty> (type:T, filter?:Filter): Ref<KillData<StateType<T>>> {
|
||||||
let entry = this.getList(type, filter)
|
let entry = this.getList(type, filter)
|
||||||
if (entry === undefined) {
|
if (entry === undefined) {
|
||||||
entry = shallowRef<KillData<StateType<T>>>({ data: {} })
|
entry = shallowRef<KillData<StateType<T>>>({ data: {}, progress: ref(0) })
|
||||||
unref(this.$state.killData[type as keyof typeof this.$state.killData] as Map<string, Ref<KillData<StateType<T>>>>).set(filter?.toURLSearchParams().toString() ?? '', entry)
|
unref(this.$state.killData[type as keyof typeof this.$state.killData] as Map<string, Ref<KillData<StateType<T>>>>).set(filter?.toURLSearchParams().toString() ?? '', entry)
|
||||||
// console.log(isRef(entry), this.$state.killData)
|
// console.log(isRef(entry), this.$state.killData)
|
||||||
}
|
}
|
||||||
@@ -181,9 +184,9 @@ export const useKillStore = defineStore('kill', {
|
|||||||
`https://tone.sleepycat.date/v2/client/${type}?` +
|
`https://tone.sleepycat.date/v2/client/${type}?` +
|
||||||
filter?.toURLSearchParams() ?? '',
|
filter?.toURLSearchParams() ?? '',
|
||||||
(progress) => {
|
(progress) => {
|
||||||
if (entry && progress !== 1) {
|
if (entry && progress !== 1 && unref(entry).progress.value !== 1) {
|
||||||
unref(entry).progress = progress
|
unref(entry).progress.value = progress
|
||||||
triggerRef(entry)
|
triggerRef(unref(entry).progress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).then(async (response) => {
|
).then(async (response) => {
|
||||||
@@ -194,7 +197,7 @@ export const useKillStore = defineStore('kill', {
|
|||||||
ref(e[1]) as Ref<StateType<T>>
|
ref(e[1]) as Ref<StateType<T>>
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
unref(entry).progress = 1
|
unref(entry).progress.value = 1
|
||||||
|
|
||||||
triggerRef(entry)
|
triggerRef(entry)
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-26
@@ -1,6 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="filters">
|
<div id="filters">
|
||||||
<span class="multiselect-wrapper">
|
<span class="multiselect-wrapper">
|
||||||
|
|
||||||
|
<!-- group-values="servers"
|
||||||
|
group-label="id"
|
||||||
|
label="name"
|
||||||
|
:group-select="true"
|
||||||
|
:options="groupedServers" -->
|
||||||
<VueMultiselect
|
<VueMultiselect
|
||||||
selectLabel=""
|
selectLabel=""
|
||||||
deselectLabel="remove"
|
deselectLabel="remove"
|
||||||
@@ -21,6 +27,7 @@
|
|||||||
deselectLabel="remove"
|
deselectLabel="remove"
|
||||||
placeholder="Select gamemode"
|
placeholder="Select gamemode"
|
||||||
v-model="model.gamemode"
|
v-model="model.gamemode"
|
||||||
|
:custom-label="((e: any) => gamemodeLocale[e] || e)"
|
||||||
:options="sortedGamemodeList"
|
:options="sortedGamemodeList"
|
||||||
:allow-empty="true"
|
:allow-empty="true"
|
||||||
:close-on-select="false"
|
:close-on-select="false"
|
||||||
@@ -43,7 +50,7 @@
|
|||||||
</VueMultiselect>
|
</VueMultiselect>
|
||||||
<button @click="model.weapon = undefined" :disabled="!model.weapon">X</button>
|
<button @click="model.weapon = undefined" :disabled="!model.weapon">X</button>
|
||||||
</span>
|
</span>
|
||||||
|
<button @click="applyFilters" :disabled="filters.server == model.server && filters.gamemode == model.gamemode && filters.weapon == model.weapon">Apply Filters</button>
|
||||||
<span class="multiselect-wrapper">
|
<span class="multiselect-wrapper">
|
||||||
<VueMultiselect
|
<VueMultiselect
|
||||||
:options-limit="20"
|
:options-limit="20"
|
||||||
@@ -57,7 +64,6 @@
|
|||||||
></VueMultiselect>
|
></VueMultiselect>
|
||||||
<button @click="playerHighlighted = undefined" :disabled="!playerHighlighted">X</button>
|
<button @click="playerHighlighted = undefined" :disabled="!playerHighlighted">X</button>
|
||||||
</span>
|
</span>
|
||||||
<button @click="applyFilters" :disabled="filters.server == model.server && filters.gamemode == model.gamemode && filters.weapon == model.weapon">Apply Filters</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="playerView">
|
<div id="playerView">
|
||||||
@@ -79,6 +85,10 @@
|
|||||||
:playerHighlighted="playerHighlighted?.id"
|
:playerHighlighted="playerHighlighted?.id"
|
||||||
>
|
>
|
||||||
</WeaponChart>
|
</WeaponChart>
|
||||||
|
<GamemodeChart
|
||||||
|
:filters="{ player: playerHighlighted?.id, ...filters }"
|
||||||
|
:playerHighlighted="playerHighlighted?.id"
|
||||||
|
></GamemodeChart>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -87,24 +97,31 @@ import { Player, Weapon, Server, Kill, useKillStore, Filter } from '@/stores/kil
|
|||||||
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 WeaponChart from '@/components/WeaponChart.vue'
|
import WeaponChart from '@/components/WeaponChart.vue'
|
||||||
|
import GamemodeChart from '@/components/GamemodeChart.vue'
|
||||||
import { Ref, defineComponent, unref } from 'vue'
|
import { Ref, defineComponent, unref } from 'vue'
|
||||||
import VueMultiselect from 'vue-multiselect'
|
import VueMultiselect from 'vue-multiselect'
|
||||||
import 'vue-multiselect/dist/vue-multiselect.css'
|
import 'vue-multiselect/dist/vue-multiselect.css'
|
||||||
import weapons from '../stores/weapons.json'
|
import weapons from '../stores/weapons.json'
|
||||||
|
import gamemodes from '../stores/gamemodes.json'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
|
function filterOutNull<T> (value:T | null):value is T {
|
||||||
|
return value !== null
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PlayerView',
|
name: 'PlayerView',
|
||||||
components: {
|
components: {
|
||||||
VueMultiselect,
|
VueMultiselect,
|
||||||
PlayerList,
|
PlayerList,
|
||||||
PlayerChart,
|
PlayerChart,
|
||||||
WeaponChart
|
WeaponChart,
|
||||||
|
GamemodeChart
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
model: { server: undefined, weapon: undefined } as unknown as {
|
model: { } as unknown as {
|
||||||
weapon: string[] | undefined;
|
weapon: string[] | undefined;
|
||||||
server: string[] | undefined;
|
server: string[] | undefined;
|
||||||
gamemode: string[] | undefined;
|
gamemode: string[] | undefined;
|
||||||
@@ -112,7 +129,8 @@ export default defineComponent({
|
|||||||
filters: new Filter(),
|
filters: new Filter(),
|
||||||
store: useKillStore(),
|
store: useKillStore(),
|
||||||
playerHighlighted: undefined as (Player & { id: string }) | undefined,
|
playerHighlighted: undefined as (Player & { id: string }) | undefined,
|
||||||
weaponLocale: weapons as { [key: string]: string }
|
weaponLocale: weapons as { [key: string]: string },
|
||||||
|
gamemodeLocale: gamemodes as { [key: string]: string }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
@@ -128,10 +146,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
servers (): { [key: string]: Ref<Server> } {
|
servers (): { [key: string]: Ref<Server> } {
|
||||||
const filter = new Filter(this.filters)
|
const data = this.store.getList('servers')?.value.data
|
||||||
delete filter.server
|
if (!data) return unref(this.store.fetch('servers')).data
|
||||||
const data = this.store.getList('servers', filter)?.value.data
|
|
||||||
if (!data) return unref(this.store.fetch('servers', filter)).data
|
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
groupedServers () {
|
groupedServers () {
|
||||||
@@ -149,11 +165,11 @@ export default defineComponent({
|
|||||||
return hosts
|
return hosts
|
||||||
},
|
},
|
||||||
weapons (): { [key: string]: Ref<Weapon> } {
|
weapons (): { [key: string]: Ref<Weapon> } {
|
||||||
const filter = new Filter(this.filters)
|
// const filter = new Filter(this.filters)
|
||||||
delete filter.weapon
|
// delete filter.weapon
|
||||||
delete filter.player
|
// delete filter.player
|
||||||
const data = this.store.getList('weapons', filter)?.value.data
|
const data = this.store.getList('weapons')?.value.data
|
||||||
if (!data) return this.store.fetch('weapons', filter).value.data
|
if (!data) return this.store.fetch('weapons').value.data
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
players (): { [key: string]: Ref<Player> } {
|
players (): { [key: string]: Ref<Player> } {
|
||||||
@@ -164,11 +180,11 @@ export default defineComponent({
|
|||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
gamemodes (): { [key: string]: Ref<Kill> } {
|
gamemodes (): { [key: string]: Ref<Kill> } {
|
||||||
const filter = new Filter(this.filters)
|
// const filter = new Filter(this.filters)
|
||||||
delete filter.gamemode
|
// delete filter.gamemode
|
||||||
delete filter.player
|
// delete filter.player
|
||||||
const data = this.store.getList('gamemodes', filter)?.value.data
|
const data = this.store.getList('gamemodes')?.value.data
|
||||||
if (!data) return this.store.fetch('gamemodes', filter).value.data
|
if (!data) return this.store.fetch('gamemodes').value.data
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
sortedWeaponList (): string[] {
|
sortedWeaponList (): string[] {
|
||||||
@@ -204,6 +220,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
model: {
|
||||||
|
handler () {
|
||||||
|
if (this.model.gamemode?.length === 0) this.model.gamemode = undefined
|
||||||
|
if (this.model.server?.length === 0) this.model.server = undefined
|
||||||
|
if (this.model.weapon?.length === 0) this.model.weapon = undefined
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
players () {
|
players () {
|
||||||
const player = this.$route.query.player?.toString()
|
const player = this.$route.query.player?.toString()
|
||||||
if (player && this.playerHighlighted?.id !== player) {
|
if (player && this.playerHighlighted?.id !== player) {
|
||||||
@@ -255,22 +279,24 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.filters.weapon !== this.$route.query.weapon) {
|
if (this.filters.weapon !== this.$route.query.weapon) {
|
||||||
this.model.weapon = [this.$route.query.weapon].flat().filter(e => e ?? false).map(e => e!.toString())
|
if (this.$route.query.weapon) this.model.weapon = [this.$route.query.weapon].flat().filter(filterOutNull).map(e => e.toString())
|
||||||
|
else this.model.weapon = undefined
|
||||||
}
|
}
|
||||||
if (this.filters.server !== this.$route.query.server) {
|
if (this.filters.server !== this.$route.query.server) {
|
||||||
if (this.$route.query.server) this.model.server = [this.$route.query.server].flat().filter(e => e ?? false).map(e => e!.toString())
|
if (this.$route.query.server) this.model.server = [this.$route.query.server].flat().filter(filterOutNull).map(e => e.toString())
|
||||||
else this.model.server = undefined
|
else this.model.server = undefined
|
||||||
}
|
}
|
||||||
if (this.filters.gamemode !== this.$route.query.gamemode) {
|
if (this.filters.gamemode !== this.$route.query.gamemode) {
|
||||||
if (this.$route.query.gamemode) this.model.gamemode = [this.$route.query.gamemode].flat().filter(e => e ?? false).map(e => e!.toString())
|
if (this.$route.query.gamemode) this.model.gamemode = [this.$route.query.gamemode].flat().filter(filterOutNull).map(e => e.toString())
|
||||||
else this.model.gamemode = undefined
|
else this.model.gamemode = undefined
|
||||||
}
|
}
|
||||||
|
this.applyFilters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style>
|
||||||
.multiselect {
|
.multiselect {
|
||||||
margin: 0 0.5em 1em 0.5em;
|
margin: 0 0.5em 1em 0.5em;
|
||||||
}
|
}
|
||||||
@@ -290,9 +316,9 @@ export default defineComponent({
|
|||||||
display: grid;
|
display: grid;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"list chart"
|
"list chart chart"
|
||||||
"list info";
|
"list info1 info2";
|
||||||
grid-template-columns: 50% 50%;
|
grid-template-columns: 50% 25% 25%;
|
||||||
grid-template-rows: 50% 50%;
|
grid-template-rows: 50% 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,11 +345,34 @@ export default defineComponent({
|
|||||||
|
|
||||||
.playerChart {
|
.playerChart {
|
||||||
display: none;
|
display: none;
|
||||||
|
grid-area: "chart";
|
||||||
}
|
}
|
||||||
|
|
||||||
.weaponChart {
|
.weaponChart {
|
||||||
display: none;
|
display: none;
|
||||||
|
grid-area: 'info1';
|
||||||
}
|
}
|
||||||
|
.gamemodeChart{
|
||||||
|
display:none;
|
||||||
|
grid-area: 'info2';
|
||||||
|
}
|
||||||
|
.playerTable{
|
||||||
|
grid-area: 'list';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerChart {
|
||||||
|
grid-area: chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weaponChart {
|
||||||
|
grid-area: info1;
|
||||||
|
}
|
||||||
|
.gamemodeChart{
|
||||||
|
grid-area: info2;
|
||||||
|
}
|
||||||
|
.playerTable{
|
||||||
|
grid-area: list;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters {
|
#filters {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
|
"noImplicitAny": false,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user