forked from CSGOWTF/csgowtf
upgrade from webpack to vite + typescript
This commit is contained in:
643
src/views/PlayerView.vue
Normal file
643
src/views/PlayerView.vue
Normal file
@@ -0,0 +1,643 @@
|
||||
<template>
|
||||
<div :style="{ minHeight: pHeight + 'px' }" class="wrapper">
|
||||
<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"
|
||||
:placeholder="
|
||||
store.state.playerDetails.matches
|
||||
? 'ShareCode (optional)'
|
||||
: 'ShareCode (required)'
|
||||
"
|
||||
:required="!store.state.playerDetails.matches"
|
||||
class="form-control bg-secondary"
|
||||
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 "/src/utils";
|
||||
import { FOOTER_HEIGHT, NAV_HEIGHT } from "/src/constants";
|
||||
import MatchesTable from "/src/components/MatchesTable";
|
||||
import router from "/src/router";
|
||||
import PlayerSideInfo from "/src/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: 0.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>
|
||||
Reference in New Issue
Block a user