mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
chore(profile): moved player avatar and its logic to separate component
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="player-avatar">
|
||||||
|
<img
|
||||||
|
v-if="avatarId"
|
||||||
|
class="player-avatar-image"
|
||||||
|
ref="avatarImageRef"
|
||||||
|
:src="`https://td2.info.pl/index.php?action=dlattach;attach=${avatarId};type=avatar`"
|
||||||
|
alt="player image"
|
||||||
|
@load="onAvatarLoadSuccess"
|
||||||
|
@error="onAvatarLoadError"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<img
|
||||||
|
v-if="avatarLoadingStatus == Status.Data.Error || avatarId == 0"
|
||||||
|
class="img-placeholder"
|
||||||
|
height="100"
|
||||||
|
src="/images/default-avatar.jpg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Loading v-else-if="avatarLoadingStatus == Status.Data.Loading || avatarId === undefined" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { Status } from '../../typings/common';
|
||||||
|
import Loading from '../Global/Loading.vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
avatarId: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const avatarImageRef = ref<HTMLImageElement | null>(null);
|
||||||
|
const avatarLoadingStatus = ref<Status.Data>(Status.Data.Loading);
|
||||||
|
|
||||||
|
function onAvatarLoadSuccess() {
|
||||||
|
if (!avatarImageRef.value) return;
|
||||||
|
|
||||||
|
avatarLoadingStatus.value = Status.Data.Loaded;
|
||||||
|
avatarImageRef.value.style.opacity = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAvatarLoadError() {
|
||||||
|
if (!avatarImageRef.value) return;
|
||||||
|
|
||||||
|
avatarLoadingStatus.value = Status.Data.Error;
|
||||||
|
avatarImageRef.value.src = '/images/default-avatar.jpg';
|
||||||
|
avatarImageRef.value.style.opacity = '1';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.player-avatar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
min-height: 110px;
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
top: 50%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.player-avatar-image {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,14 +2,7 @@
|
|||||||
<section class="profile-summary">
|
<section class="profile-summary">
|
||||||
<div class="player-info">
|
<div class="player-info">
|
||||||
<div class="info-main">
|
<div class="info-main">
|
||||||
<img
|
<ProfilePlayerAvatar :avatarId="playerTD2Info?.avatar" />
|
||||||
v-if="playerTD2Info"
|
|
||||||
:src="`https://td2.info.pl/index.php?action=dlattach;attach=${playerTD2Info.avatar};type=avatar`"
|
|
||||||
alt="player image"
|
|
||||||
@error="(e) => ((e.target as any).src = '/images/default-avatar.jpg')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<img class="img-placeholder" height="100" src="/images/default-avatar.jpg" v-else />
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 class="player-name-header" :class="{ 'text--donator': isPlayerDonator }">
|
<h2 class="player-name-header" :class="{ 'text--donator': isPlayerDonator }">
|
||||||
@@ -228,7 +221,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, PropType } from 'vue';
|
import { computed, onMounted, PropType, ref } from 'vue';
|
||||||
import { API, Td2API } from '../../typings/api';
|
import { API, Td2API } from '../../typings/api';
|
||||||
import { calculateExpStyles } from '../../composables/badge';
|
import { calculateExpStyles } from '../../composables/badge';
|
||||||
import { getCountPercentage } from '../../utils/calcUtils';
|
import { getCountPercentage } from '../../utils/calcUtils';
|
||||||
@@ -237,6 +230,10 @@ import { useRoute } from 'vue-router';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useApiStore } from '../../store/apiStore';
|
import { useApiStore } from '../../store/apiStore';
|
||||||
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { Status } from '../../typings/common';
|
||||||
|
import Loading from '../Global/Loading.vue';
|
||||||
|
import ProfilePlayerAvatar from './ProfilePlayerAvatar.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -249,19 +246,21 @@ const props = defineProps({
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|
||||||
playerTD2Info: {
|
|
||||||
type: Object as PropType<Td2API.UsersInfoByName.UserInfo | null>
|
|
||||||
},
|
|
||||||
|
|
||||||
playerName: {
|
playerName: {
|
||||||
type: String
|
type: String
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const playerTD2Info = ref<Td2API.UsersInfoByName.UserInfo | null>(null);
|
||||||
|
|
||||||
const isPlayerDonator = computed(() =>
|
const isPlayerDonator = computed(() =>
|
||||||
props.playerName ? apiStore.donatorsData.includes(props.playerName) : false
|
props.playerName ? apiStore.donatorsData.includes(props.playerName) : false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchTD2Data();
|
||||||
|
});
|
||||||
|
|
||||||
const activeDispatches = computed(() => {
|
const activeDispatches = computed(() => {
|
||||||
if (!props.playerName) return [];
|
if (!props.playerName) return [];
|
||||||
if (!apiStore.activeData || !apiStore.activeData.activeSceneries) return [];
|
if (!apiStore.activeData || !apiStore.activeData.activeSceneries) return [];
|
||||||
@@ -280,6 +279,27 @@ const activeTrains = computed(() => {
|
|||||||
(t) => t.driverName == props.playerName && (t.lastSeen >= Date.now() - 60000 || t.online)
|
(t) => t.driverName == props.playerName && (t.lastSeen >= Date.now() - 60000 || t.online)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function fetchTD2Data() {
|
||||||
|
if (!props.playerName) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get<Td2API.UsersInfoByName.Response>('https://api.td2.info.pl', {
|
||||||
|
params: {
|
||||||
|
method: 'getUsersInfoByName',
|
||||||
|
name: props.playerName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.success && response.data.message.length == 1) {
|
||||||
|
playerTD2Info.value = response.data.message[0];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -293,23 +313,6 @@ const activeTrains = computed(() => {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-stats {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1em;
|
|
||||||
|
|
||||||
hr {
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.player-info,
|
|
||||||
.player-stats > div {
|
|
||||||
background-color: var(--clr-tile);
|
|
||||||
border-radius: 0.5em;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.player-name-header {
|
.player-name-header {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
|
|
||||||
@@ -327,14 +330,6 @@ const activeTrains = computed(() => {
|
|||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-journal-links {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5em;
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-container {
|
.badge-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -348,11 +343,12 @@ const activeTrains = computed(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-header {
|
.player-journal-links {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.25em;
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-activity {
|
.info-activity {
|
||||||
@@ -362,22 +358,49 @@ const activeTrains = computed(() => {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
|
||||||
|
.dispatcher-badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-badge {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
gap: 0.25em;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-activity > .dispatcher-badge {
|
.player-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
gap: 0.25em;
|
gap: 1em;
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-activity > .driver-badge {
|
.player-info,
|
||||||
display: flex;
|
.player-stats > div {
|
||||||
align-items: center;
|
background-color: var(--clr-tile);
|
||||||
gap: 0.25em;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
padding: 0.25em 0.5em;
|
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include responsive.midScreen {
|
@include responsive.midScreen {
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="profile-view">
|
<div class="profile-view">
|
||||||
<div class="profile-wrapper" v-if="playerInfo && playerDataStatus == Status.Data.Loaded">
|
<div class="profile-wrapper" v-if="playerInfo && playerDataStatus == Status.Data.Loaded">
|
||||||
<ProfileSummary
|
<ProfileSummary :playerInfo="playerInfo" :playerName="playerName" />
|
||||||
:playerInfo="playerInfo"
|
|
||||||
:playerTD2Info="playerTD2Info"
|
|
||||||
:playerName="playerName"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="profile-side">
|
<div class="profile-side">
|
||||||
<ProfileRecentStats :playerInfo="playerInfo" />
|
<ProfileRecentStats :playerInfo="playerInfo" />
|
||||||
@@ -49,7 +45,6 @@ const route = useRoute();
|
|||||||
const playerName = ref('');
|
const playerName = ref('');
|
||||||
|
|
||||||
const playerInfo = ref<API.PlayerInfo.Data | null>(null);
|
const playerInfo = ref<API.PlayerInfo.Data | null>(null);
|
||||||
const playerTD2Info = ref<Td2API.UsersInfoByName.UserInfo | null>(null);
|
|
||||||
const playerDataStatus = ref(Status.Data.Initialized);
|
const playerDataStatus = ref(Status.Data.Initialized);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -67,7 +62,6 @@ async function fetchAllData() {
|
|||||||
const playerId = route.query.playerId?.toString();
|
const playerId = route.query.playerId?.toString();
|
||||||
|
|
||||||
playerInfo.value = null;
|
playerInfo.value = null;
|
||||||
playerTD2Info.value = null;
|
|
||||||
playerDataStatus.value = Status.Data.Loading;
|
playerDataStatus.value = Status.Data.Loading;
|
||||||
|
|
||||||
if (!playerId) {
|
if (!playerId) {
|
||||||
@@ -92,12 +86,7 @@ async function fetchAllData() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const playerTd2InfoResponse = await fetchPlayerTD2Info(playerName.value);
|
|
||||||
|
|
||||||
playerInfo.value = playerInfoResponse;
|
playerInfo.value = playerInfoResponse;
|
||||||
playerTD2Info.value = playerTd2InfoResponse;
|
|
||||||
// playerJournal.value = playerJournalResponse;
|
|
||||||
|
|
||||||
playerDataStatus.value = Status.Data.Loaded;
|
playerDataStatus.value = Status.Data.Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,27 +107,6 @@ async function fetchPlayerInfoData(playerId: string) {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchPlayerTD2Info(playerName: string) {
|
|
||||||
if (!apiStore.client || !playerName) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios.get<Td2API.UsersInfoByName.Response>('https://api.td2.info.pl', {
|
|
||||||
params: {
|
|
||||||
method: 'getUsersInfoByName',
|
|
||||||
name: playerName
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.success && response.data.message.length == 1) {
|
|
||||||
return response.data.message[0];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user