add progress bar
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="loadingComponent">
|
||||||
|
<progress class="loadingBar" max="1" :value="value"></progress>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
value: Number
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
.loadingComponent {
|
||||||
|
display:flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: calc(100% - 3em);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="playerChart" ref="container">
|
<div class="playerChart" ref="container">
|
||||||
<!-- <Scatter :data="chart" :options="chartOptions" /> -->
|
<LoadingBar v-if="progress!==1" :value="progress"></LoadingBar>
|
||||||
<Scatter :data="chart" :options="chartOptions" />
|
<Scatter :data="chart" :options="chartOptions" v-if="progress===1" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -26,6 +26,8 @@ import { defineComponent, PropType, Ref, toRaw, triggerRef, 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'
|
||||||
|
|
||||||
|
import LoadingBar from './LoadingBar.vue'
|
||||||
|
|
||||||
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
|
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -43,12 +45,19 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ['highlightPlayer'],
|
emits: ['highlightPlayer'],
|
||||||
components: {
|
components: {
|
||||||
Scatter
|
Scatter, LoadingBar
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.refreshColors++
|
this.refreshColors++
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
progress () {
|
||||||
|
if (this.filters) {
|
||||||
|
const { player: _, ...withoutPlayer } = this.filters
|
||||||
|
return this.store.getPlayerList(withoutPlayer)?.value.progress
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
playerList (): (Player & {id:string})[] {
|
playerList (): (Player & {id:string})[] {
|
||||||
const data = this.store.getPlayerList(this.filters || {})?.value.data
|
const data = this.store.getPlayerList(this.filters || {})?.value.data
|
||||||
if (!data) return Object.entries(this.store.fetchPlayers(this.filters || {}).value.data).map(e => ({ id: e[0], ...toRaw(e[1].value) }))
|
if (!data) return Object.entries(this.store.fetchPlayers(this.filters || {}).value.data).map(e => ({ id: e[0], ...toRaw(e[1].value) }))
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
<span v-on:click="updateSort('avg_distance')"
|
<span v-on:click="updateSort('avg_distance')"
|
||||||
:class="sortingData.argument == 'avg_distance' ? 'selected' : ''">Average distance</span>
|
:class="sortingData.argument == 'avg_distance' ? 'selected' : ''">Average distance</span>
|
||||||
</div>
|
</div>
|
||||||
<VirtualList :list="playerList" :row-height="32" v-slot="slotProps" :highlighted="playerHighlighted">
|
<LoadingBar v-if="progress!==1" :value="progress"></LoadingBar>
|
||||||
|
<VirtualList :list="playerList" :row-height="32" v-slot="slotProps" :highlighted="playerHighlighted" v-if="progress===1">
|
||||||
<div :class="'playerRow ' + (slotProps.data.id === playerHighlighted ? 'selected ' : '') + ((slotProps.index + 1)%2 ? 'odd ':'uneven ')" v-on:click="$emit('highlightPlayer', slotProps.data.id)">
|
<div :class="'playerRow ' + (slotProps.data.id === playerHighlighted ? 'selected ' : '') + ((slotProps.index + 1)%2 ? 'odd ':'uneven ')" v-on:click="$emit('highlightPlayer', slotProps.data.id)">
|
||||||
<div><span>{{ slotProps.index+1 }}</span></div>
|
<div><span>{{ slotProps.index+1 }}</span></div>
|
||||||
<div><span>{{ slotProps.data.value.username }}</span></div>
|
<div><span>{{ slotProps.data.value.username }}</span></div>
|
||||||
@@ -30,9 +31,10 @@
|
|||||||
import { defineComponent, PropType, Ref } from 'vue'
|
import { defineComponent, PropType, Ref } from 'vue'
|
||||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||||
import VirtualList from './List/VirtualList.vue'
|
import VirtualList from './List/VirtualList.vue'
|
||||||
|
import LoadingBar from './LoadingBar.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { VirtualList },
|
components: { VirtualList, LoadingBar },
|
||||||
props: {
|
props: {
|
||||||
filters: { type: Object as PropType<Filters>, default: () => ({}) },
|
filters: { type: Object as PropType<Filters>, default: () => ({}) },
|
||||||
playerHighlighted: String
|
playerHighlighted: String
|
||||||
@@ -45,6 +47,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
progress () {
|
||||||
|
const { player: _, ...withoutPlayer } = this.filters
|
||||||
|
return this.store.getPlayerList(withoutPlayer)?.value.progress
|
||||||
|
},
|
||||||
players (): { [key: string]: Ref<Player> } {
|
players (): { [key: string]: Ref<Player> } {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { player: _, ...withoutPlayer } = this.filters
|
const { player: _, ...withoutPlayer } = this.filters
|
||||||
@@ -61,7 +67,6 @@ export default defineComponent({
|
|||||||
return player
|
return player
|
||||||
}) as (Ref<Player> & { id: string })[]
|
}) as (Ref<Player> & { id: string })[]
|
||||||
|
|
||||||
const date = new Date()
|
|
||||||
if (this.sortingData.argument === 'username') {
|
if (this.sortingData.argument === 'username') {
|
||||||
const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' })
|
const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' })
|
||||||
players.sort((a, b) => {
|
players.sort((a, b) => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="weaponChart" ref="container">
|
<div class="weaponChart" ref="container">
|
||||||
<Doughnut :data="chart" :options="chartOptions" />
|
<LoadingBar v-if="progress !== 1" :value="progress"></LoadingBar>
|
||||||
|
<Doughnut :data="chart" :options="chartOptions" v-if="progress === 1"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -12,6 +13,8 @@ import { Doughnut } from 'vue-chartjs'
|
|||||||
import { defineComponent, PropType, Ref } from 'vue'
|
import { defineComponent, PropType, Ref } from 'vue'
|
||||||
import weapons from '../stores/weapons.json'
|
import weapons from '../stores/weapons.json'
|
||||||
|
|
||||||
|
import LoadingBar from './LoadingBar.vue'
|
||||||
|
|
||||||
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
|
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -22,7 +25,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ['highlightPlayer'],
|
emits: ['highlightPlayer'],
|
||||||
components: {
|
components: {
|
||||||
Doughnut
|
Doughnut, LoadingBar
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.refreshColors++
|
this.refreshColors++
|
||||||
@@ -34,6 +37,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
progress () {
|
||||||
|
const { weapon: _, ...withoutWeapon } = this.filters || {}
|
||||||
|
return this.store.getWeaponList(withoutWeapon)?.value.progress
|
||||||
|
},
|
||||||
colors () {
|
colors () {
|
||||||
// eslint-disable-next-line no-unused-expressions
|
// eslint-disable-next-line no-unused-expressions
|
||||||
this.refreshColors
|
this.refreshColors
|
||||||
|
|||||||
+90
-18
@@ -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 {
|
export interface Kill {
|
||||||
deaths: number;
|
deaths: number;
|
||||||
kills: number;
|
kills: number;
|
||||||
@@ -38,6 +91,7 @@ export interface Kill {
|
|||||||
export interface KillData<T extends Kill> {
|
export interface KillData<T extends Kill> {
|
||||||
filter: Filters;
|
filter: Filters;
|
||||||
data: { [key: string]: Ref<T> };
|
data: { [key: string]: Ref<T> };
|
||||||
|
progress?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Player extends Kill {
|
export interface Player extends Kill {
|
||||||
@@ -106,14 +160,20 @@ export const useKillStore = defineStore('kill', {
|
|||||||
entry = shallowRef({ filter, data: {} })
|
entry = shallowRef({ filter, data: {} })
|
||||||
this.players.push(entry)
|
this.players.push(entry)
|
||||||
}
|
}
|
||||||
fetch(
|
fetchWithLoading(
|
||||||
'https://tone.sleepycat.date/v2/client/players?' +
|
'https://tone.sleepycat.date/v2/client/players?' +
|
||||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
new URLSearchParams(filter as Record<keyof Filters, string>),
|
||||||
|
(progress) => {
|
||||||
|
if (entry) {
|
||||||
|
unref(entry).progress = progress
|
||||||
|
if (progress !== 1) triggerRef(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
).then(async response => {
|
).then(async response => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
if (entry) {
|
||||||
unref(entry)!.data = Object.fromEntries(Object.entries(await response.json()).map(e => [e[0], ref(e[1] as Player)]))
|
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)
|
||||||
triggerRef(entry!)
|
}
|
||||||
})
|
})
|
||||||
return entry
|
return entry
|
||||||
},
|
},
|
||||||
@@ -125,14 +185,20 @@ export const useKillStore = defineStore('kill', {
|
|||||||
this.weapons.push(entry)
|
this.weapons.push(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(
|
fetchWithLoading(
|
||||||
'https://tone.sleepycat.date/v2/client/weapons?' +
|
'https://tone.sleepycat.date/v2/client/weapons?' +
|
||||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
new URLSearchParams(filter as Record<keyof Filters, string>),
|
||||||
|
(progress) => {
|
||||||
|
if (entry) {
|
||||||
|
unref(entry).progress = progress
|
||||||
|
if (progress !== 1) triggerRef(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
).then(async response => {
|
).then(async response => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
if (entry) {
|
||||||
unref(entry)!.data = Object.fromEntries(Object.entries(await response.json()).map(e => [e[0], ref(e[1] as Weapon)]))
|
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)
|
||||||
triggerRef(entry!)
|
}
|
||||||
})
|
})
|
||||||
return entry
|
return entry
|
||||||
},
|
},
|
||||||
@@ -143,14 +209,20 @@ export const useKillStore = defineStore('kill', {
|
|||||||
entry = shallowRef({ filter, data: {} })
|
entry = shallowRef({ filter, data: {} })
|
||||||
this.servers.push(entry)
|
this.servers.push(entry)
|
||||||
}
|
}
|
||||||
fetch(
|
fetchWithLoading(
|
||||||
'https://tone.sleepycat.date/v2/client/servers?' +
|
'https://tone.sleepycat.date/v2/client/servers?' +
|
||||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
new URLSearchParams(filter as Record<keyof Filters, string>),
|
||||||
|
(progress) => {
|
||||||
|
if (entry) {
|
||||||
|
unref(entry).progress = progress
|
||||||
|
if (progress !== 1) triggerRef(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
).then(async response => {
|
).then(async response => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
if (entry) {
|
||||||
unref(entry)!.data = Object.fromEntries(Object.entries(await response.json()).map(e => [e[0], ref(e[1] as Server)]))
|
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)
|
||||||
triggerRef(entry!)
|
}
|
||||||
})
|
})
|
||||||
return entry
|
return entry
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user