cache into map

This commit is contained in:
2023-06-20 20:46:14 +02:00
parent a6e75006e6
commit 7d38564107
10 changed files with 216 additions and 344 deletions
+2 -2
View File
@@ -14,8 +14,8 @@ module.exports = {
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',
indent: 'off', '@typescript-eslint/indent': ['error', 2],
'@typescript-eslint/indent': ['error', 2] camelcase: 0
} }
} }
-65
View File
@@ -1,65 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component'
@Options({
props: {
msg: String
}
})
export default class HelloWorld extends Vue {
msg!: string
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
+9 -7
View File
@@ -19,7 +19,7 @@ import {
} from 'chart.js' } from 'chart.js'
import annotationPlugin, { AnnotationPluginOptions } from 'chartjs-plugin-annotation' import annotationPlugin, { AnnotationPluginOptions } from 'chartjs-plugin-annotation'
import dataLabel from 'chartjs-plugin-datalabels' import dataLabel from 'chartjs-plugin-datalabels'
import { useKillStore, Player, Filters } 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, Ref, toRaw, triggerRef, unref } from 'vue'
@@ -33,7 +33,7 @@ ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annota
export default defineComponent({ export default defineComponent({
name: 'PlayerChart', name: 'PlayerChart',
props: { props: {
filters: Object as PropType<Filters>, filters: Object as PropType<Filter>,
playerHighlighted: String playerHighlighted: String
}, },
data: () => { data: () => {
@@ -53,14 +53,17 @@ export default defineComponent({
computed: { computed: {
progress () { progress () {
if (this.filters) { if (this.filters) {
const { player: _, ...withoutPlayer } = this.filters const filter = new Filter(this.filters)
return this.store.getPlayerList(withoutPlayer)?.value.progress delete filter.player
return this.store.getList('players', filter)?.value.progress
} }
return 0 return 0
}, },
playerList (): (Player & {id:string})[] { playerList (): (Player & {id:string})[] {
const data = this.store.getPlayerList(this.filters || {})?.value.data const filter = new Filter(this.filters)
if (!data) return Object.entries(this.store.fetchPlayers(this.filters || {}).value.data).map(e => ({ id: e[0], ...toRaw(e[1].value) })) delete filter.player
const data = unref(this.store.getList('players', filter))?.data
if (!data) return Object.entries(this.store.fetch('players', filter).value.data).map(e => ({ id: e[0], ...toRaw(e[1].value) }))
const cut = Object.entries(data).map(e => ({ id: e[0], ...toRaw(e[1].value) })) const cut = Object.entries(data).map(e => ({ id: e[0], ...toRaw(e[1].value) }))
cut.sort((a, b) => { cut.sort((a, b) => {
return b.kills - a.kills return b.kills - a.kills
@@ -140,7 +143,6 @@ export default defineComponent({
}, },
onClick: (e: ChartEvent, element: any) => { onClick: (e: ChartEvent, element: any) => {
this.$emit('highlightPlayer', element.length > 0 ? this.playerList[element[0].index].id : undefined) this.$emit('highlightPlayer', element.length > 0 ? this.playerList[element[0].index].id : undefined)
if (this.store.getPlayerList(this.filters || {}) !== undefined) triggerRef(this.store.getPlayerList(this.filters || {}) as unknown as Ref)
}, },
onHover: (e: any, element: any) => { onHover: (e: any, element: any) => {
if (!e.native.target) return if (!e.native.target) return
+22 -32
View File
@@ -28,15 +28,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, Ref } from 'vue' import { defineComponent, PropType, Ref, isRef } from 'vue'
import { useKillStore, Player, Filters } 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 },
props: { props: {
filters: { type: Object as PropType<Filters>, default: () => ({}) }, filters: { type: Object as PropType<Filter>, default: () => ({}) },
playerHighlighted: String playerHighlighted: String
}, },
emits: ['highlightPlayer'], emits: ['highlightPlayer'],
@@ -48,17 +49,26 @@ export default defineComponent({
}, },
computed: { computed: {
progress () { progress () {
const { player: _, ...withoutPlayer } = this.filters const filter = new Filter(this.filters)
return this.store.getPlayerList(withoutPlayer)?.value.progress delete filter.player
return this.store.getList('players', filter)?.value.progress
}, },
players (): { [key: string]: Ref<Player> } { players (): { [key: string]: Ref<Player> } {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const filter = new Filter(this.filters)
const { player: _, ...withoutPlayer } = this.filters delete filter.player
const data = this.store.getPlayerList(withoutPlayer)?.value.data const data = this.store.getList('players', filter)?.value.data
if (!data) return this.store.fetchPlayers(withoutPlayer).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 })[] {
const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' })
const sortingFunctions = {
'k/d': ({ _rawValue: aVal }, { _rawValue: bVal }) => aVal.kills / Math.max(1, aVal.deaths) - bVal.kills / Math.max(1, bVal.deaths),
avg_distance: ({ _rawValue: aVal }, { _rawValue: bVal }) => ((aVal.total_distance / aVal.kills) || 0) - ((bVal.total_distance / bVal.kills) || 0),
username: ({ _rawValue: aVal }, { _rawValue: bVal }) => collator.compare(aVal.username, bVal.username)
} as {[k: string]: (a:Ref<Player>, b:Ref<Player>)=>number}
if (!this.players) return [] if (!this.players) return []
const players = Object.entries(this.players).map(e => { const players = Object.entries(this.players).map(e => {
const target = {} as ProxyHandler<Ref<Player>> const target = {} as ProxyHandler<Ref<Player>>
@@ -67,30 +77,10 @@ export default defineComponent({
return player return player
}) as (Ref<Player> & { id: string })[] }) as (Ref<Player> & { id: string })[]
if (this.sortingData.argument === 'username') { if (this.sortingData.argument in sortingFunctions) {
const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }) players.sort(sortingFunctions[this.sortingData.argument])
players.sort((a, b) => {
// Hack because toRaw is slow as fuck
const aVal = a._rawValue
const bVal = b._rawValue
return collator.compare(aVal.username, bVal.username)
})
} else if (this.sortingData.argument === 'k/d') {
players.sort((a, b) => {
// Hack because toRaw is slow as fuck
const aVal = a._rawValue
const bVal = b._rawValue
return aVal.kills / Math.max(1, aVal.deaths) - bVal.kills / Math.max(1, bVal.deaths)
})
} else if (this.sortingData.argument === 'avg_distance') {
players.sort((a, b) => {
// Hack because toRaw is slow as fuck
const aVal = a._rawValue
const bVal = b._rawValue
return ((aVal.total_distance / aVal.kills) || 0) - ((bVal.total_distance / bVal.kills) || 0)
})
} else { } else {
const argument = this.sortingData.argument const argument = this.sortingData.argument as Exclude<keyof Player, 'username' | 'avg_distance'>
players.sort((a, b) => { players.sort((a, b) => {
// Hack because toRaw is slow as fuck // Hack because toRaw is slow as fuck
const aVal = a._rawValue const aVal = a._rawValue
+15 -12
View File
@@ -8,9 +8,9 @@
<script lang="ts"> <script lang="ts">
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js' import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
import dataLabel from 'chartjs-plugin-datalabels' import dataLabel from 'chartjs-plugin-datalabels'
import { Weapon, Filters, useKillStore } from '@/stores/kill' import { Weapon, Filter, useKillStore } from '@/stores/kill'
import { Doughnut } from 'vue-chartjs' import { Doughnut } from 'vue-chartjs'
import { defineComponent, PropType, Ref } from 'vue' import { defineComponent, PropType, Ref, unref } from 'vue'
import weapons from '../stores/weapons.json' import weapons from '../stores/weapons.json'
import LoadingBar from './LoadingBar.vue' import LoadingBar from './LoadingBar.vue'
@@ -20,7 +20,7 @@ ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
export default defineComponent({ export default defineComponent({
name: 'WeaponChart', name: 'WeaponChart',
props: { props: {
filters: Object as PropType<Filters>, filters: Object as PropType<Filter>,
playerHighlighted: String playerHighlighted: String
}, },
emits: ['highlightPlayer'], emits: ['highlightPlayer'],
@@ -38,9 +38,10 @@ export default defineComponent({
}, },
computed: { computed: {
progress () { progress () {
const { weapon: _, ...withoutWeapon } = this.filters || {} const filter = new Filter(this.filters)
const data = this.store.getWeaponList(withoutWeapon)?.value delete filter.weapon
if (!data) return this.store.fetchWeapons(withoutWeapon).value.progress const data = this.store.getList('weapons', filter)?.value
if (!data) return this.store.fetch('weapons', filter).value.progress
return data.progress return data.progress
}, },
colors () { colors () {
@@ -148,19 +149,21 @@ export default defineComponent({
return options return options
}, },
weapons (): { [key: string]: Ref<Weapon> } { weapons (): { [key: string]: Ref<Weapon> } {
const { weapon: _, ...withoutWeapon } = this.filters || {} const filter = new Filter({ ...this.filters, player: this.playerHighlighted })
const data = this.store.getWeaponList(withoutWeapon)?.value.data delete filter.weapon
if (!data) return this.store.fetchWeapons(withoutWeapon).value.data const data = this.store.getList('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[] {
if (!this.weapons) return [] if (!this.weapons) return []
const weapons = Object.keys(this.weapons).filter(e => this.weapons[e].value.kills > 0) const weapons = Object.keys(this.weapons).filter(e => unref(this.weapons[e]).kills > 0)
weapons.sort((a, b) => { weapons.sort((a, b) => {
if (Number(this.weapons[a].value.kills) < Number(this.weapons[b].value.kills)) { if (Number(this.weapons[a].value.kills) < Number(unref(this.weapons[b]).kills)) {
return -1 return -1
} }
if (Number(this.weapons[a].value.kills) > Number(this.weapons[b].value.kills)) { if (Number(this.weapons[a].value.kills) > Number(unref(this.weapons[b]).kills)) {
return 1 return 1
} }
return 0 return 0
+9 -10
View File
@@ -3,7 +3,7 @@ import { createApp, ref, unref } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import { Kill, KillData, objectEqual, useKillStore } from './stores/kill' import { Filter, Kill, KillData, useKillStore } from './stores/kill'
const pinia = createPinia() const pinia = createPinia()
@@ -48,22 +48,21 @@ const socket = new WebSocket('wss://tone.sleepycat.date/v2/client/websocket')
socket.onmessage = function (e) { socket.onmessage = function (e) {
if (e.data === 'ping') return socket.send('pong') if (e.data === 'ping') return socket.send('pong')
const data = JSON.parse(e.data) const data = JSON.parse(e.data)
registerWebSocketKill(data) // registerWebSocketKill(data)
} }
function registerWebSocketKill (data : websocketData) { function registerWebSocketKill (data : websocketData) {
const { player: _, ...filterWithoutPlayer } = store.$state.currentFilter const { player: _, ...filterWithoutPlayer } = store.$state.currentFilter
const list = unref(store.$state.players.find((e) => { const filter = new Filter(filterWithoutPlayer)
return objectEqual(filterWithoutPlayer, e.value.filter) const list = unref(store.getList('players', filter))
}))
if (!list) return if (!list) return
if (!(!list.filter.server || list.filter.server.includes(data.servername))) return if (!(!filter.server || filter.server.includes(data.servername))) return
const WeaponFilter = (!list.filter.weapon || list.filter.weapon.includes(data.cause_of_death)) const WeaponFilter = (!filter.weapon || filter.weapon.includes(data.cause_of_death))
const CurrentWeaponFilter = list.filter.weapon?.includes(data.attacker_current_weapon) const CurrentWeaponFilter = filter.weapon?.includes(data.attacker_current_weapon)
if (!(WeaponFilter || CurrentWeaponFilter)) return if (!(WeaponFilter || CurrentWeaponFilter)) return
if (!(!list.filter.gamemode || list.filter.gamemode.includes(data.game_mode))) return if (!(!filter.gamemode || filter.gamemode.includes(data.game_mode))) return
if (!list.data[data.victim_id]) { if (!list.data[data.victim_id]) {
list.data[data.victim_id] = ref({ list.data[data.victim_id] = ref({
@@ -99,7 +98,7 @@ function registerWebSocketKill (data : websocketData) {
unref(list.data[data.victim_id]).username = data.victim_name unref(list.data[data.victim_id]).username = data.victim_name
unref(list.data[data.attacker_id]).username = data.attacker_name unref(list.data[data.attacker_id]).username = data.attacker_name
const serverlist = store.getServerList({}) const serverlist = store.getList('servers')
if (serverlist) { if (serverlist) {
const server = unref(unref(serverlist).data[data.servername]) const server = unref(unref(serverlist).data[data.servername])
if (server) { if (server) {
+135 -187
View File
@@ -1,110 +1,105 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import * as assert from 'assert' import { Ref, ref, shallowRef, triggerRef, unref, isRef } from 'vue'
import { Ref, ref, shallowRef, triggerRef, unref } from 'vue'
export class Filter {
constructor (filt?:{server?:string[], player?: string, weapon?: string[], host?: string[], map?: string[], gamemode?: string[]}) {
if (filt?.server) this.server = filt.server
if (filt?.player) this.player = filt.player
if (filt?.weapon) this.weapon = filt.weapon
if (filt?.host) this.host = filt.host
if (filt?.map) this.map = filt.map
if (filt?.gamemode) this.gamemode = filt.gamemode
}
export interface Filters {
server?: string[]; server?: string[];
player?: string; player?: string;
weapon?: string[]; weapon?: string[];
host?: string[]; host?: string[];
map?: string[]; map?: string[];
gamemode?: string[]; gamemode?: string[];
} toURLSearchParams (): URLSearchParams {
return new URLSearchParams(
export function removeNullEntries (a: Filters) { Object.entries(this).filter(e => e[1] !== undefined)
return Object.fromEntries( .map<string[][]>((e) => [e[1]].flat().map((v) => [e[0], v]))
Object.entries(a).filter((e) => e[1] !== undefined && e[1] !== null) .flat(1)
) )
}
export function objectEqual (a: Filters, b: Filters) {
try {
a = removeNullEntries(a)
b = removeNullEntries(b)
assert.deepStrictEqual(a, b)
return true
} catch {
return false
} }
} }
function fetchWithLoading (url: string, progress: (percentage: number) => void) { function fetchWithLoading (url: string, progress: (percentage: number) => void) {
return fetch(url).then(response => { return fetch(url)
if (!response.ok) { .then((response) => {
throw Error(response.status + ' ' + response.statusText) if (!response.ok) {
} throw Error(response.status + ' ' + response.statusText)
}
if (!response.body) { if (!response.body) {
throw Error('ReadableStream not yet supported in this browser.') throw Error('ReadableStream not yet supported in this browser.')
} }
// to access headers, server must send CORS header "Access-Control-Expose-Headers: content-encoding, content-length x-file-size" // to access headers, server must send CORS header "Access-Control-Expose-Headers: content-encoding, content-length x-file-size"
// server must send custom x-file-size header if gzip or other content-encoding is used // server must send custom x-file-size header if gzip or other content-encoding is used
const contentEncoding = response.headers.get('Content-Encoding') const contentEncoding = response.headers.get('Content-Encoding')
const contentLength = response.headers.get(contentEncoding ? 'X-File-Size' : 'content-length') const contentLength = response.headers.get(
if (contentLength === null) { contentEncoding ? 'X-File-Size' : 'content-length'
throw Error('Response size header unavailable') )
} if (contentLength === null) {
throw Error('Response size header unavailable')
}
const total = parseInt(contentLength, 10) const total = parseInt(contentLength, 10)
let loaded = 0 let loaded = 0
return new Response( return new Response(
new ReadableStream({ new ReadableStream({
start (controller) { start (controller) {
const reader = response.body!.getReader() const reader = response.body!.getReader()
read() read()
function read () { function read () {
reader.read().then(({ done, value }) => { reader
if (done) { .read()
controller.close() .then(({ done, value }) => {
progress(1) if (done) {
return controller.close()
} progress(1)
if (value) { return
loaded += value.byteLength }
progress(loaded / total) if (value) {
controller.enqueue(value) loaded += value.byteLength
} progress(loaded / total)
read() controller.enqueue(value)
}).catch(error => { }
console.error(error) read()
controller.error(error) })
}) .catch((error) => {
console.error(error)
controller.error(error)
})
}
} }
} })
}) )
) })
}) .catch((error) => {
.catch(error => {
console.error(error) console.error(error)
}) })
} }
export interface Kill { export interface Kill {
deaths: number; deaths: number;
// eslint-disable-next-line camelcase deaths_while_equipped: number;
deaths_while_equipped:number
kills: number; kills: number;
// eslint-disable-next-line camelcase
max_distance: number; max_distance: number;
// eslint-disable-next-line camelcase
total_distance: number; total_distance: number;
} }
export interface KillData<T extends Kill> {
filter: Filters;
data: { [key: string]: Ref<T> };
progress?: number;
}
export interface Player extends Kill { export interface Player extends Kill {
username: string; username: string;
// eslint-disable-next-line camelcase
// deaths_while_equipped?: number; // deaths_while_equipped?: number;
} }
export interface Weapon extends Kill { export interface Weapon extends Kill {
// eslint-disable-next-line camelcase
deaths_while_equipped: number; deaths_while_equipped: number;
} }
@@ -112,6 +107,11 @@ export interface Server extends Kill {
host: number; host: number;
} }
export interface KillData<T extends Kill> {
data: { [key: string]: Ref<T> };
progress?: number;
}
export interface NSServer { export interface NSServer {
name: string; name: string;
region: string; region: string;
@@ -121,160 +121,108 @@ export interface NSServer {
map: string; map: string;
playlist: string; playlist: string;
hasPassword: boolean; hasPassword: boolean;
modInfo: { Mods: [] } modInfo: { Mods: [] };
} }
// define your typings for the store state // define your typings for the store state
export interface State { export interface State {
servers: Ref<KillData<Server>>[]; killData:{
players: Ref<KillData<Player>>[]; servers: Ref<Map<string, Ref<KillData<Server>>>>;
weapons: Ref<KillData<Weapon>>[]; players: Ref<Map<string, Ref<KillData<Player>>>>;
maps: Ref<KillData<Kill>>[]; weapons: Ref<Map<string, Ref<KillData<Weapon>>>>;
gamemodes: Ref<KillData<Kill>>[]; maps: Ref<Map<string, Ref<KillData<Kill>>>>;
gamemodes: Ref<Map<string, Ref<KillData<Kill>>>>;
},
hosts: { [key: number]: string }; hosts: { [key: number]: string };
nsServers: NSServer[] | undefined; nsServers: NSServer[] | undefined;
currentFilter:Filters; currentFilter: Filter;
} }
type StateProperty = keyof State['killData']
type StateType<T extends StateProperty> =
T extends 'servers' ? Server :
T extends 'players' ? Player :
T extends 'weapons' ? Weapon :
Kill
let serverInterval: number let serverInterval: number
export const useKillStore = defineStore('kill', { export const useKillStore = defineStore('kill', {
state: (): State => ({ state: (): State => ({
servers: [], killData: {
players: [], servers: shallowRef(new Map()),
weapons: [], players: shallowRef(new Map()),
maps: [], weapons: shallowRef(new Map()),
gamemodes: [], maps: shallowRef(new Map()),
gamemodes: shallowRef(new Map())
},
hosts: {}, hosts: {},
nsServers: undefined, nsServers: undefined,
currentFilter: {} currentFilter: new Filter()
}), }),
getters: { getters: {
getPlayerList: (state) => (filters: Filters) => { getList:
return state.players.find((e) => objectEqual(unref(e).filter, filters)) (state) =>
}, <T extends StateProperty>(type: T, filters?: Filter): Ref<KillData<StateType<T>>> | undefined => {
getWeaponList: (state) => (filters: Filters) => return unref(state.killData[type as keyof typeof state.killData]).get(filters?.toURLSearchParams().toString() ?? '') as Ref<KillData<StateType<T>>>
state.weapons.find((e) => objectEqual(unref(e).filter, filters)), },
getServerList: (state) => (filters: Filters) =>
state.servers.find((e) => objectEqual(unref(e).filter, filters)),
getGamemodeList: (state) => (filters: Filters) =>
state.gamemodes.find((e) => objectEqual(unref(e).filter, filters)),
getNSServers: (state) => state.nsServers getNSServers: (state) => state.nsServers
}, },
actions: { actions: {
fetchPlayers (filter: Filters) { fetch <T extends StateProperty> (type:T, filter?:Filter): Ref<KillData<StateType<T>>> {
filter = removeNullEntries(filter) let entry = this.getList(type, filter)
let entry = this.players.find((e) => objectEqual(unref(e).filter, filter))
if (entry === undefined) { if (entry === undefined) {
entry = shallowRef({ filter, data: {} }) entry = shallowRef<KillData<StateType<T>>>({ data: {} })
this.players.push(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)
} }
fetchWithLoading( fetchWithLoading(
'https://tone.sleepycat.date/v2/client/players?' + `https://tone.sleepycat.date/v2/client/${type}?` +
new URLSearchParams(Object.entries(filter).map<string[][]>(e => [e[1]].flat().map(v => [e[0], v])).flat(1)), filter?.toURLSearchParams() ?? '',
(progress) => { (progress) => {
if (entry && unref(entry).progress !== 1) { if (entry && progress !== 1) {
unref(entry).progress = progress unref(entry).progress = progress
if (progress !== 1) triggerRef(entry) triggerRef(entry)
} }
} }
).then(async response => { ).then(async (response) => {
if (entry) { if (entry) {
unref(entry).data = Object.fromEntries(Object.entries(await response?.json()).map(e => [e[0], ref(e[1] as Player)])) entry.value.data = Object.fromEntries(
triggerRef(entry) Object.entries(await response?.json()).map<[string, Ref<StateType<T>>]>((e) => [
} e[0],
}) ref(e[1]) as Ref<StateType<T>>
return entry ])
}, )
fetchWeapons (filter: Filters) { unref(entry).progress = 1
filter = removeNullEntries(filter)
let entry = this.weapons.find((e) => objectEqual(unref(e).filter, filter))
if (!entry) {
entry = shallowRef({ filter, data: {} })
this.weapons.push(entry)
}
fetchWithLoading(
'https://tone.sleepycat.date/v2/client/weapons?' +
new URLSearchParams(Object.entries(filter).map<string[][]>(e => [e[1]].flat().map(v => [e[0], v])).flat(1)),
(progress) => {
if (entry && unref(entry).progress !== 1) {
unref(entry).progress = progress
if (progress !== 1) triggerRef(entry)
}
}
).then(async response => {
if (entry) {
unref(entry).data = Object.fromEntries(Object.entries(await response?.json()).map(e => [e[0], ref(e[1] as Weapon)]))
triggerRef(entry)
}
})
return entry
},
fetchServers (filter: Filters) {
filter = removeNullEntries(filter)
let entry = this.servers.find((e) => objectEqual(unref(e).filter, filter))
if (!entry) {
entry = shallowRef({ filter, data: {} })
this.servers.push(entry)
}
fetchWithLoading(
'https://tone.sleepycat.date/v2/client/servers?' +
new URLSearchParams(Object.entries(filter).map<string[][]>(e => [e[1]].flat().map(v => [e[0], v])).flat(1)),
(progress) => {
if (entry && unref(entry).progress !== 1) {
unref(entry).progress = progress
if (progress !== 1) triggerRef(entry)
}
}
).then(async response => {
if (entry) {
unref(entry).data = Object.fromEntries(Object.entries(await response?.json()).map(e => [e[0], ref(e[1] as Server)]))
triggerRef(entry)
}
})
return entry
},
fetchGamemodes (filter: Filters) {
filter = removeNullEntries(filter)
let entry = this.gamemodes.find((e) => objectEqual(unref(e).filter, filter))
if (!entry) {
entry = shallowRef({ filter, data: {} })
this.gamemodes.push(entry)
}
fetchWithLoading(
'https://tone.sleepycat.date/v2/client/gamemodes?' +
new URLSearchParams(Object.entries(filter).map<string[][]>(e => [e[1]].flat().map(v => [e[0], v])).flat(1)),
(progress) => {
if (entry && unref(entry).progress !== 1) {
unref(entry).progress = progress
if (progress !== 1) triggerRef(entry)
}
}
).then(async response => {
if (entry) {
unref(entry).data = Object.fromEntries(Object.entries(await response?.json()).map(e => [e[0], ref(e[1] as Kill)]))
triggerRef(entry) triggerRef(entry)
} }
}) })
return entry return entry
}, },
fetchNSServers () { fetchNSServers () {
fetch( fetch('https://northstar.tf/client/servers').then(async (response) => {
'https://northstar.tf/client/servers').then(async response => {
this.nsServers = await response.json() this.nsServers = await response.json()
}) })
clearInterval(serverInterval) clearInterval(serverInterval)
serverInterval = setInterval(() => this.fetchNSServers(), 60000) serverInterval = setInterval(() => this.fetchNSServers(), 60000)
return this.nsServers || [] return this.nsServers || []
}, },
setFilter (filter:Filters) { setFilter (filter: Filter) {
if (this.currentFilter.player === filter.player) {
const newfilter = new Filter(filter)
delete newfilter.player
this.fetch('players', newfilter)
}
if (this.currentFilter.weapon === filter.weapon) {
const newfilter = new Filter(filter)
delete newfilter.weapon
this.fetch('weapons', newfilter)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { player: _player, ...withoutPlayers } = filter // const { server: _server, ...withoutServer } = filter
if (this.currentFilter.player === _player) { this.fetchPlayers(withoutPlayers) }
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { weapon: _weapon, ...withoutWeapon } = filter
if (this.currentFilter.weapon === _weapon) { this.fetchWeapons(withoutWeapon) }
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { server: _server, ...withoutServer } = filter
// if (this.currentFilter.server === _server) { this.fetchServers(withoutServer) } // if (this.currentFilter.server === _server) { this.fetchServers(withoutServer) }
this.currentFilter = filter this.currentFilter = filter
} }
-5
View File
@@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
+22 -22
View File
@@ -61,14 +61,14 @@
</div> </div>
<div id="playerView"> <div id="playerView">
<PlayerList <PlayerList
:filters="filters" :filters="filters"
v-on:highlightPlayer="highlight_player" v-on:highlightPlayer="highlight_player"
:playerHighlighted="playerHighlighted?.id" :playerHighlighted="playerHighlighted?.id"
> >
</PlayerList> </PlayerList>
<PlayerChart <PlayerChart
:filters="filters" :filters="filters"
v-on:highlightPlayer="highlight_player" v-on:highlightPlayer="highlight_player"
:playerHighlighted="playerHighlighted?.id" :playerHighlighted="playerHighlighted?.id"
@@ -83,7 +83,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Player, Weapon, Server, Kill, useKillStore, Filters } from '@/stores/kill' import { Player, Weapon, Server, Kill, useKillStore, Filter } from '@/stores/kill'
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'
@@ -109,7 +109,7 @@ export default defineComponent({
server: string[] | undefined; server: string[] | undefined;
gamemode: string[] | undefined; gamemode: string[] | undefined;
}, },
filters: {} as Filters, 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 }
@@ -128,10 +128,10 @@ export default defineComponent({
}, },
computed: { computed: {
servers (): { [key: string]: Ref<Server> } { servers (): { [key: string]: Ref<Server> } {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const filter = new Filter(this.filters)
const { server: _, player: _1, ...withoutServer } = this.filters delete filter.server
const data = this.store.getServerList(withoutServer)?.value.data const data = this.store.getList('servers', filter)?.value.data
if (!data) return unref(this.store.fetchServers(withoutServer)).data if (!data) return unref(this.store.fetch('servers', filter)).data
return data return data
}, },
groupedServers () { groupedServers () {
@@ -149,24 +149,26 @@ export default defineComponent({
return hosts return hosts
}, },
weapons (): { [key: string]: Ref<Weapon> } { weapons (): { [key: string]: Ref<Weapon> } {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const filter = new Filter(this.filters)
const { weapon: _, player: _1, ...withoutWeapons } = this.filters delete filter.weapon
const data = this.store.getWeaponList(withoutWeapons)?.value.data delete filter.player
if (!data) return this.store.fetchWeapons(withoutWeapons).value.data const data = this.store.getList('weapons', filter)?.value.data
if (!data) return this.store.fetch('weapons', filter).value.data
return data return data
}, },
players (): { [key: string]: Ref<Player> } { players (): { [key: string]: Ref<Player> } {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const filter = new Filter(this.filters)
const { player: _, ...withoutPlayers } = this.filters delete filter.player
const data = this.store.getPlayerList(withoutPlayers)?.value.data const data = this.store.getList('players', filter)?.value.data
if (!data) return this.store.fetchPlayers(withoutPlayers).value.data if (!data) return this.store.fetch('players', filter).value.data
return data return data
}, },
gamemodes (): { [key: string]: Ref<Kill> } { gamemodes (): { [key: string]: Ref<Kill> } {
// eslint-disable-next-line @typescript-eslint/no-unused-vars const filter = new Filter(this.filters)
const { gamemode: _, player: _1, ...withoutGamemode } = this.filters delete filter.gamemode
const data = this.store.getGamemodeList(withoutGamemode)?.value.data delete filter.player
if (!data) return this.store.fetchGamemodes(withoutGamemode).value.data const data = this.store.getList('gamemodes', filter)?.value.data
if (!data) return this.store.fetch('gamemodes', filter).value.data
return data return data
}, },
sortedWeaponList (): string[] { sortedWeaponList (): string[] {
@@ -263,8 +265,6 @@ export default defineComponent({
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(e => e ?? false).map(e => e!.toString())
else this.model.gamemode = undefined else this.model.gamemode = undefined
} }
console.log(this.model, this.$route.query)
} }
} }
}) })
+2 -2
View File
@@ -52,8 +52,8 @@ export default defineComponent({
}, },
computed: { computed: {
servers (): { [key: string]: Ref<Server> } { servers (): { [key: string]: Ref<Server> } {
const data = this.store.getServerList({})?.value.data const data = this.store.getList('servers')?.value.data
if (!data) return unref(this.store.fetchServers({})).data if (!data) return unref(this.store.fetch('servers')).data
return data return data
}, },
serverList (): (Ref<Server> & { name: string })[] { serverList (): (Ref<Server> & { name: string })[] {