{{ slotProps.index+1 }}
{{ slotProps.data.value.username }}
@@ -30,9 +31,10 @@
import { defineComponent, PropType, Ref } from 'vue'
import { useKillStore, Player, Filters } from '@/stores/kill'
import VirtualList from './List/VirtualList.vue'
+import LoadingBar from './LoadingBar.vue'
export default defineComponent({
- components: { VirtualList },
+ components: { VirtualList, LoadingBar },
props: {
filters: { type: Object as PropType
, default: () => ({}) },
playerHighlighted: String
@@ -45,6 +47,10 @@ export default defineComponent({
}
},
computed: {
+ progress () {
+ const { player: _, ...withoutPlayer } = this.filters
+ return this.store.getPlayerList(withoutPlayer)?.value.progress
+ },
players (): { [key: string]: Ref } {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { player: _, ...withoutPlayer } = this.filters
@@ -61,7 +67,6 @@ export default defineComponent({
return player
}) as (Ref & { id: string })[]
- const date = new Date()
if (this.sortingData.argument === 'username') {
const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' })
players.sort((a, b) => {
diff --git a/src/components/WeaponChart.vue b/src/components/WeaponChart.vue
index 4a57e9c..c5fd856 100644
--- a/src/components/WeaponChart.vue
+++ b/src/components/WeaponChart.vue
@@ -1,6 +1,7 @@
-
+
+
@@ -12,6 +13,8 @@ import { Doughnut } from 'vue-chartjs'
import { defineComponent, PropType, Ref } from 'vue'
import weapons from '../stores/weapons.json'
+import LoadingBar from './LoadingBar.vue'
+
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
export default defineComponent({
@@ -22,7 +25,7 @@ export default defineComponent({
},
emits: ['highlightPlayer'],
components: {
- Doughnut
+ Doughnut, LoadingBar
},
mounted () {
this.refreshColors++
@@ -34,6 +37,10 @@ export default defineComponent({
}
},
computed: {
+ progress () {
+ const { weapon: _, ...withoutWeapon } = this.filters || {}
+ return this.store.getWeaponList(withoutWeapon)?.value.progress
+ },
colors () {
// eslint-disable-next-line no-unused-expressions
this.refreshColors
diff --git a/src/stores/kill.ts b/src/stores/kill.ts
index f993c3a..96b082e 100644
--- a/src/stores/kill.ts
+++ b/src/stores/kill.ts
@@ -27,6 +27,59 @@ function objectEqual (a: Filters, b: Filters) {
}
}
+function fetchWithLoading (url: string, progress: (percentage: number) => void) {
+ return fetch(url).then(response => {
+ if (!response.ok) {
+ throw Error(response.status + ' ' + response.statusText)
+ }
+
+ if (!response.body) {
+ 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"
+ // server must send custom x-file-size header if gzip or other content-encoding is used
+ const contentEncoding = response.headers.get('Content-Encoding')
+ const contentLength = response.headers.get(contentEncoding ? 'X-File-Size' : 'content-length')
+ if (contentLength === null) {
+ throw Error('Response size header unavailable')
+ }
+
+ const total = parseInt(contentLength, 10)
+ let loaded = 0
+
+ return new Response(
+ new ReadableStream({
+ start (controller) {
+ const reader = response.body!.getReader()
+
+ read()
+ function read () {
+ reader.read().then(({ done, value }) => {
+ if (done) {
+ controller.close()
+ return
+ }
+ if (value) {
+ loaded += value.byteLength
+ progress(loaded / total)
+ controller.enqueue(value)
+ }
+ read()
+ }).catch(error => {
+ console.error(error)
+ controller.error(error)
+ })
+ }
+ }
+ })
+ )
+ })
+ .catch(error => {
+ console.error(error)
+ })
+}
+
export interface Kill {
deaths: number;
kills: number;
@@ -38,6 +91,7 @@ export interface Kill {
export interface KillData {
filter: Filters;
data: { [key: string]: Ref };
+ progress?: number;
}
export interface Player extends Kill {
@@ -56,15 +110,15 @@ export interface Server extends Kill {
}
export interface NSServer {
- name:string;
- region:string;
- description:string;
- playerCount:number;
- maxPlayers:number;
- map:string;
- playlist:string;
- hasPassword:boolean;
- modInfo:{Mods:[]}
+ name: string;
+ region: string;
+ description: string;
+ playerCount: number;
+ maxPlayers: number;
+ map: string;
+ playlist: string;
+ hasPassword: boolean;
+ modInfo: { Mods: [] }
}
// define your typings for the store state
export interface State {
@@ -77,7 +131,7 @@ export interface State {
nsServers: NSServer[] | undefined
}
-let serverInterval:number
+let serverInterval: number
export const useKillStore = defineStore('kill', {
state: (): State => ({
servers: [],
@@ -106,14 +160,20 @@ export const useKillStore = defineStore('kill', {
entry = shallowRef({ filter, data: {} })
this.players.push(entry)
}
- fetch(
+ fetchWithLoading(
'https://tone.sleepycat.date/v2/client/players?' +
- new URLSearchParams(filter as Record)
+ new URLSearchParams(filter as Record),
+ (progress) => {
+ if (entry) {
+ unref(entry).progress = progress
+ if (progress !== 1) triggerRef(entry)
+ }
+ }
).then(async response => {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- unref(entry)!.data = Object.fromEntries(Object.entries(await response.json()).map(e => [e[0], ref(e[1] as Player)]))
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- triggerRef(entry!)
+ if (entry) {
+ unref(entry).data = Object.fromEntries(Object.entries(await response?.json()).map(e => [e[0], ref(e[1] as Player)]))
+ triggerRef(entry)
+ }
})
return entry
},
@@ -125,14 +185,20 @@ export const useKillStore = defineStore('kill', {
this.weapons.push(entry)
}
- fetch(
+ fetchWithLoading(
'https://tone.sleepycat.date/v2/client/weapons?' +
- new URLSearchParams(filter as Record)
+ new URLSearchParams(filter as Record),
+ (progress) => {
+ if (entry) {
+ unref(entry).progress = progress
+ if (progress !== 1) triggerRef(entry)
+ }
+ }
).then(async response => {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- unref(entry)!.data = Object.fromEntries(Object.entries(await response.json()).map(e => [e[0], ref(e[1] as Weapon)]))
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- triggerRef(entry!)
+ 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
},
@@ -143,14 +209,20 @@ export const useKillStore = defineStore('kill', {
entry = shallowRef({ filter, data: {} })
this.servers.push(entry)
}
- fetch(
+ fetchWithLoading(
'https://tone.sleepycat.date/v2/client/servers?' +
- new URLSearchParams(filter as Record)
+ new URLSearchParams(filter as Record),
+ (progress) => {
+ if (entry) {
+ unref(entry).progress = progress
+ if (progress !== 1) triggerRef(entry)
+ }
+ }
).then(async response => {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- unref(entry)!.data = Object.fromEntries(Object.entries(await response.json()).map(e => [e[0], ref(e[1] as Server)]))
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- triggerRef(entry!)
+ 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
},