forked from CSGOWTF/csgowtf
544 lines
16 KiB
Vue
544 lines
16 KiB
Vue
<template>
|
|
<div class="wrapper" :style="{minHeight: pHeight + 'px'}">
|
|
<div class="container-lg">
|
|
<div v-if="store.state.playerDetails.name">
|
|
<div class="card mb-3 bg-transparent border-0">
|
|
<div class="row g-0">
|
|
<div class="img-container col-md-2 pt-3">
|
|
<img
|
|
:class="data.tracked ? 'tracked' : ''"
|
|
:src="constructAvatarUrl(store.state.playerDetails.avatar, 'full')"
|
|
:title="data.tracked ? 'Tracked' : ''"
|
|
alt="Player avatar"
|
|
class="img-fluid avatar">
|
|
</div>
|
|
<div class="col-md-8 d-flex">
|
|
<div class="card-body">
|
|
<h3 class="card-title"><a
|
|
:href="/^\d{17}$/.test(props.id) ? 'https://steamcommunity.com/profiles/' + props.id : 'https://steamcommunity.com/id/' + props.id"
|
|
class="text-decoration-none text-white"
|
|
target="_blank"
|
|
title="Open steam profile">{{
|
|
store.state.playerDetails.name
|
|
}}
|
|
<i class="fa fa-steam"></i>
|
|
</a></h3>
|
|
<table class="table table-borderless text-center">
|
|
<tr>
|
|
<th class="wlt-win text-uppercase text-muted">Wins</th>
|
|
<th class="wlt-loss text-uppercase text-muted">Losses</th>
|
|
<th class="wlt-tie text-uppercase text-muted">Ties</th>
|
|
<th class="wlt-win-rate text-uppercase text-muted">Win-Rate</th>
|
|
<th class="wlt-tie-rate text-uppercase text-muted">Tie-Rate</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="wlt-win">{{ data.match_stats.win }}</td>
|
|
<td class="wlt-loss">{{ data.match_stats.loss }}</td>
|
|
<td class="wlt-tie">{{ data.match_stats.tie }}</td>
|
|
<td class="wlt-win-rate">{{
|
|
data.match_stats.win > 0 ? (data.match_stats.win / data.match_stats.total * 100).toFixed(0) : 0
|
|
}}%
|
|
</td>
|
|
<td class="wlt-tie-rate">{{
|
|
data.match_stats.tie > 0 ? (data.match_stats.tie / data.match_stats.total * 100).toFixed(0) : 0
|
|
}}%
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div class="badges">
|
|
<img v-if="store.state.playerDetails.vac"
|
|
:title="'VAC-Ban: ' + FormatVacDate(store.state.playerDetails.vac_date, store.state.matchDetails.date)"
|
|
alt="Vac banned"
|
|
src="/images/icons/vac_banned.svg">
|
|
<img v-if="store.state.playerDetails.game_ban"
|
|
:title="'Game-Ban: ' + FormatVacDate(store.state.playerDetails.game_ban_date, store.state.matchDetails.date)"
|
|
alt="Game banned"
|
|
src="/images/icons/game_banned.svg">
|
|
</div>
|
|
</div>
|
|
<div v-if="!data.tracked" class="dropdown trackme-btn">
|
|
<button
|
|
id="login-dropdown"
|
|
aria-expanded="false"
|
|
class="btn border-2 btn-outline-info"
|
|
data-bs-toggle="dropdown"
|
|
type="button"
|
|
>
|
|
Track Me!
|
|
</button>
|
|
<div aria-labelledby="login-dropdown" class="dropdown-menu mt-2 border-2 border-primary bg-body"
|
|
style="width: 320px">
|
|
<form class="px-4 py-3">
|
|
<!-- AuthCode input -->
|
|
<div class="form-outline mb-4">
|
|
<input id="track-authcode" v-model="data.userData.authcode" class="form-control bg-secondary"
|
|
placeholder="AuthCode (required)"
|
|
required type="text"/>
|
|
</div>
|
|
|
|
<!-- ShareCode input -->
|
|
<div class="form-outline mb-2">
|
|
<input id="track-sharecode" v-model="data.userData.sharecode" class="form-control bg-secondary"
|
|
:placeholder="store.state.playerDetails.matches ? 'ShareCode (optional)' : 'ShareCode (required)'"
|
|
:required="!store.state.playerDetails.matches"
|
|
type="text"/>
|
|
</div>
|
|
|
|
<div class="form-outline mb-4">
|
|
<small>
|
|
<a href="https://help.steampowered.com/en/wizard/HelpWithGameIssue/?appid=730&issueid=128"
|
|
target="_blank">
|
|
Here you can find your AuthCode and ShareCode
|
|
</a>
|
|
</small>
|
|
</div>
|
|
|
|
<!-- Submit button -->
|
|
<button class="btn btn-outline-warning border-2" type="submit"
|
|
@click.prevent="TrackPlayer">
|
|
TrackMe
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div v-if="data.tracked" class="refresh-btn" title="Refresh Match-List" @click="RefreshData">
|
|
<i class="fa fa-refresh fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="match-container d-flex">
|
|
<div class="matches">
|
|
|
|
<MatchesTable v-if="store.state.playerDetails.matches" :matches="store.state.playerDetails.matches" color-front />
|
|
<h5 v-else>Track yourself to see your matches</h5>
|
|
|
|
</div>
|
|
|
|
<div v-if="store.state.playerDetails.matches" class="side-info-container">
|
|
|
|
<PlayerSideInfo :player_meta="data.playerMeta"/>
|
|
|
|
</div>
|
|
</div>
|
|
<div class="load-more col-lg-9 col-md-12 text-center">
|
|
<button v-if="data.match_stats.total !== data.matches.length" :key="scrollToPos(store.state.scroll_state)"
|
|
class="btn border-2 btn-outline-info" @click="setMoreMatches">Load More
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else class="text-center pt-5">
|
|
<h3>Player-Page</h3>
|
|
<hr>
|
|
<h6>There seems to be a problem loading the player</h6>
|
|
<h6>Please try again later</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch} from "vue";
|
|
import {useStore} from "vuex";
|
|
import {
|
|
constructAvatarUrl,
|
|
DisplayRank,
|
|
FixMapName,
|
|
FormatVacDate,
|
|
GetPlayerMeta,
|
|
GetUser,
|
|
GetWinLoss,
|
|
GoToPlayer,
|
|
LoadImage,
|
|
LoadMoreMatches,
|
|
MatchNotParsedTime,
|
|
ProcessName,
|
|
SaveLastVisitedToLocalStorage,
|
|
scrollToPos,
|
|
setTitle,
|
|
TrackMe
|
|
} from "@/utils";
|
|
import {FOOTER_HEIGHT, NAV_HEIGHT} from "@/constants";
|
|
import MatchesTable from "@/components/MatchesTable";
|
|
import router from "@/router";
|
|
import PlayerSideInfo from "@/components/PlayerSideInfo";
|
|
import {StatusCodes as STATUS} from "http-status-codes";
|
|
|
|
export default {
|
|
name: 'PlayerView',
|
|
components: {PlayerSideInfo, MatchesTable},
|
|
props: ['id'],
|
|
setup(props) {
|
|
// Variables
|
|
const store = useStore()
|
|
const pHeight = ref(0)
|
|
const displayCounter = 3
|
|
|
|
const data = reactive({
|
|
userData: {
|
|
authcode: '',
|
|
sharecode: ''
|
|
},
|
|
tracked: false,
|
|
matches: [],
|
|
match_stats: {
|
|
loss: 0,
|
|
win: 0,
|
|
tie: 0,
|
|
total: 0
|
|
},
|
|
playerMeta: {},
|
|
})
|
|
|
|
const getWindowHeight = () => {
|
|
const navHeight = document.getElementsByTagName('nav')[0].clientHeight
|
|
const footerHeight = document.getElementsByTagName('footer')[0].clientHeight
|
|
|
|
// 70 = nav-height | 108.5 = footer-height
|
|
return window.innerHeight - navHeight - footerHeight
|
|
}
|
|
|
|
pHeight.value = getWindowHeight()
|
|
|
|
onBeforeMount(() => {
|
|
if (Object.entries(store.state.playerDetails).length === 0) {
|
|
GetPlayer()
|
|
} else {
|
|
// console.log(store.state.playerDetails)
|
|
SetPlayerData()
|
|
}
|
|
}
|
|
)
|
|
|
|
const SetPlayerData = async () => {
|
|
data.tracked = store.state.playerDetails.tracked
|
|
if (store.state.playerDetails.matches)
|
|
data.matches = store.state.playerDetails.matches
|
|
if (store.state.playerDetails.match_stats) {
|
|
data.match_stats.loss = store.state.playerDetails.match_stats.loss || 0
|
|
data.match_stats.win = store.state.playerDetails.match_stats.win || 0
|
|
data.match_stats.tie = store.state.playerDetails.match_stats.tie || 0
|
|
data.match_stats.total = data.match_stats.loss + data.match_stats.win + data.match_stats.tie
|
|
}
|
|
|
|
store.commit({
|
|
type: 'changeId64',
|
|
id: store.state.playerDetails.steamid64
|
|
})
|
|
store.commit({
|
|
type: 'changeVanityUrl',
|
|
id: store.state.playerDetails.vanity_url || ''
|
|
})
|
|
|
|
if (store.state.playerDetails.matches) {
|
|
if (data.matches[0].map) {
|
|
await LoadImage(data.matches[0].map)
|
|
} else if (!data.matches[0].map && MatchNotParsedTime(data.matches[0].date) && data.matches[1].map) {
|
|
await LoadImage(data.matches[1].map)
|
|
} else {
|
|
await LoadImage('random')
|
|
}
|
|
} else {
|
|
await LoadImage('random')
|
|
}
|
|
|
|
document.querySelector('.bg-img').style.display = 'initial'
|
|
document.getElementById('app').style.background = 'rgba(0, 0, 0, .7)'
|
|
|
|
let player = {
|
|
'steamid64': store.state.playerDetails.steamid64,
|
|
'vanity_url': store.state.playerDetails.vanity_url || '',
|
|
'name': store.state.playerDetails.name,
|
|
'avatar': constructAvatarUrl(store.state.playerDetails.avatar, 'medium')
|
|
}
|
|
SaveLastVisitedToLocalStorage(player)
|
|
|
|
setTitle(store.state.playerDetails.name)
|
|
}
|
|
|
|
const GetPlayer = async (reset = false) => {
|
|
if (props.id) {
|
|
const resData = await GetUser(store, props.id)
|
|
|
|
if (resData !== null) {
|
|
if (resData.steamid64 !== store.state.playerDetails.steamid64 || reset) {
|
|
resData.name = ProcessName(resData.name)
|
|
|
|
store.commit('resetPlayerDetails')
|
|
store.commit({
|
|
type: 'changePlayerDetails',
|
|
data: resData
|
|
})
|
|
}
|
|
|
|
await SetPlayerData()
|
|
}
|
|
}
|
|
}
|
|
|
|
const setMoreMatches = async () => {
|
|
const res = await LoadMoreMatches(store, store.state.playerDetails.steamid64, data.matches[data.matches.length - 1].date)
|
|
|
|
if (res !== null)
|
|
await res.matches.forEach(e => data.matches.push(e))
|
|
|
|
scrollToPos(window.scrollY)
|
|
|
|
// console.log(store.state.playerDetails)
|
|
}
|
|
|
|
const RefreshData = async () => {
|
|
const refreshButton = document.querySelector('.refresh-btn .fa')
|
|
refreshButton.classList.add('fa-spin')
|
|
refreshButton.classList.add('fa-fw')
|
|
refreshButton.classList.remove('fa-refresh')
|
|
refreshButton.classList.add('fa-spinner')
|
|
scrollToPos(0)
|
|
|
|
await GetPlayer(true).then(() => {
|
|
setTimeout(() => {
|
|
refreshButton.classList.remove('fa-spin')
|
|
refreshButton.classList.remove('fa-fw')
|
|
refreshButton.classList.add('fa-refresh')
|
|
refreshButton.classList.remove('fa-spinner')
|
|
}, 2000)
|
|
})
|
|
|
|
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter)
|
|
if (data.playerMeta === null)
|
|
data.playerMeta = {}
|
|
}
|
|
|
|
const TrackPlayer = async () => {
|
|
let message = ''
|
|
|
|
if (data.matches.length === 0) {
|
|
if (data.userData.sharecode === '') {
|
|
message = 'Sharecode is missing'
|
|
}
|
|
if (data.userData.authcode === '') {
|
|
message = 'Authcode is missing'
|
|
}
|
|
} else {
|
|
if (data.userData.authcode === '') {
|
|
message = 'Authcode is missing'
|
|
}
|
|
}
|
|
|
|
if (message !== '') {
|
|
store.commit({
|
|
type: 'changeInfoState',
|
|
data: {
|
|
statuscode: STATUS.IM_A_TEAPOT,
|
|
message: message,
|
|
type: 'error'
|
|
}
|
|
})
|
|
} else {
|
|
const res = await TrackMe(store, store.state.playerDetails.steamid64, data.userData.authcode, data.userData.sharecode)
|
|
|
|
if (res !== null && res === STATUS.ACCEPTED) {
|
|
location.reload()
|
|
}
|
|
}
|
|
}
|
|
|
|
watch(() => props.id, async () => {
|
|
await GetPlayer()
|
|
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter)
|
|
if (data.playerMeta === null)
|
|
data.playerMeta = {}
|
|
})
|
|
|
|
// watch(() => data.playerMeta, () => {
|
|
// console.log(data.playerMeta)
|
|
// })
|
|
|
|
onMounted(async () => {
|
|
const height = window.innerHeight - NAV_HEIGHT - FOOTER_HEIGHT
|
|
const wrapper = document.querySelector('.wrapper')
|
|
wrapper.style.minHeight = height + 'px'
|
|
|
|
await GetPlayer()
|
|
|
|
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter)
|
|
if (data.playerMeta === null)
|
|
data.playerMeta = {}
|
|
|
|
scrollToPos(store.state.scroll_state)
|
|
|
|
// console.log(store.state.playerDetails)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
store.commit('changeScrollState', window.scrollY)
|
|
|
|
router.beforeEach((to, from, next) => {
|
|
if (to.fullPath.match('/player/') && from.fullPath.match('/player/')) {
|
|
store.commit('changeScrollState', 0)
|
|
}
|
|
next()
|
|
})
|
|
})
|
|
|
|
window.onresize = () => {
|
|
pHeight.value = getWindowHeight()
|
|
}
|
|
|
|
return {
|
|
data,
|
|
store,
|
|
pHeight,
|
|
props,
|
|
TrackPlayer,
|
|
RefreshData,
|
|
TrackMe,
|
|
GetWinLoss,
|
|
DisplayRank,
|
|
constructAvatarUrl,
|
|
FormatVacDate,
|
|
FixMapName,
|
|
GoToPlayer,
|
|
MatchNotParsedTime,
|
|
scrollToPos,
|
|
setMoreMatches
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.wrapper {
|
|
.load-more {
|
|
padding: 1rem 0;
|
|
}
|
|
|
|
.trackme-btn,
|
|
.refresh-btn {
|
|
position: absolute;
|
|
right: 0;
|
|
bottom: 0;
|
|
}
|
|
|
|
.refresh-btn {
|
|
cursor: pointer;
|
|
|
|
&:hover,
|
|
&:focus {
|
|
.fa-refresh {
|
|
color: var(--bs-warning);
|
|
}
|
|
}
|
|
|
|
.fa {
|
|
font-size: 1.3rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
.card {
|
|
padding-top: 10px;
|
|
|
|
.badges {
|
|
height: 30px;
|
|
|
|
img {
|
|
width: auto;
|
|
height: 100%;
|
|
margin-right: 5px;
|
|
}
|
|
}
|
|
|
|
.avatar {
|
|
border-radius: 50%;
|
|
height: 150px;
|
|
width: 150px;
|
|
box-shadow: 0 0 10px black;
|
|
|
|
&.tracked {
|
|
box-shadow: 0 0 20px 5px var(--bs-success);
|
|
}
|
|
}
|
|
|
|
.fa {
|
|
font-size: .75rem;
|
|
vertical-align: top;
|
|
}
|
|
|
|
table {
|
|
max-width: 500px;
|
|
|
|
.wlt-win, .wlt-loss, .wlt-tie {
|
|
text-align: start;
|
|
max-width: 70px;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.wlt-tie-rate, .wlt-win-rate {
|
|
text-align: end;
|
|
max-width: 90px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.match-container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
|
|
.matches {
|
|
width: 75%;
|
|
}
|
|
|
|
.side-info-container {
|
|
width: 25%;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 768px) {
|
|
.card {
|
|
.avatar {
|
|
height: 75px !important;
|
|
width: 75px !important;
|
|
}
|
|
}
|
|
.trackme-btn,
|
|
.refresh-btn {
|
|
top: 25px;
|
|
}
|
|
|
|
.refresh-btn {
|
|
&:hover,
|
|
&:focus {
|
|
.fa {
|
|
color: white !important;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 991px) {
|
|
.card .avatar {
|
|
height: 120px;
|
|
width: 120px;
|
|
}
|
|
.match-container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
gap: 0;
|
|
|
|
.matches {
|
|
width: 100% !important;
|
|
}
|
|
|
|
.side-info-container {
|
|
display: none !important;
|
|
}
|
|
}
|
|
}
|
|
</style>
|