Add PlayerImage component for showing player images in various parts of the UI

This commit is contained in:
James Lyne 2022-02-05 17:23:55 +00:00
parent 0b6bf8d206
commit db41a585e6
4 changed files with 78 additions and 48 deletions

View File

@ -0,0 +1,64 @@
<!--
- Copyright 2022 James Lyne
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-->
<template>
<img width="16" height="16" :src="image" alt="" />
</template>
<script lang="ts">
import defaultImage from '@/assets/images/player_face.png';
import {computed, defineComponent, watch} from "@vue/runtime-core";
import {LiveAtlasPlayer} from "@/index";
import {onMounted, ref} from "vue";
import {useStore} from "@/store";
import {getMinecraftHead} from "@/util";
export default defineComponent({
name: 'PlayerImage',
components: {},
props: {
player: {
type: Object as () => LiveAtlasPlayer | string,
required: true
}
},
setup(props) {
const store = useStore(),
image = ref(defaultImage),
name = computed(() => typeof props.player === 'string' ? props.player : props.player.name),
imagesEnabled = computed(() => store.state.components.players.showImages),
updatePlayerImage = async () => {
image.value = defaultImage;
if (imagesEnabled.value) {
try {
const result = await getMinecraftHead(props.player, 'small');
image.value = result.src;
} catch (e) {
}
}
};
watch(name, () => updatePlayerImage());
onMounted(() => updatePlayerImage());
return {
image
}
}
});
</script>

View File

@ -16,7 +16,7 @@
<template> <template>
<li :class="`message message--${message.type}`"> <li :class="`message message--${message.type}`">
<img v-if="showFace" width="16" height="16" class="message__face" :src="image" alt="" /> <PlayerImage v-if="showFace" :player="message.playerAccount" width="16" height="16" class="message__face" />
<span v-if="messageChannel" class="message__channel" v-html="messageChannel"></span> <span v-if="messageChannel" class="message__channel" v-html="messageChannel"></span>
<span v-if="showSender" class="message__sender" v-html="message.playerName"></span> <span v-if="showSender" class="message__sender" v-html="message.playerName"></span>
<span class="message__content" v-html="messageContent"></span> <span class="message__content" v-html="messageContent"></span>
@ -24,13 +24,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, ref, onMounted, computed} from "@vue/runtime-core"; import {defineComponent, computed} from "@vue/runtime-core";
import {getMinecraftHead} from '@/util';
import {useStore} from "@/store"; import {useStore} from "@/store";
import defaultImage from '@/assets/images/player_face.png';
import {LiveAtlasChat} from "@/index"; import {LiveAtlasChat} from "@/index";
import PlayerImage from "@/components/PlayerImage.vue";
export default defineComponent({ export default defineComponent({
components: {PlayerImage},
props: { props: {
message: { message: {
type: Object as () => LiveAtlasChat, type: Object as () => LiveAtlasChat,
@ -39,8 +39,7 @@
}, },
setup(props) { setup(props) {
const store = useStore(); const store = useStore();
let image = ref(defaultImage), let showFace = computed(() => store.state.components.chatBox?.showPlayerFaces && props.message.playerAccount),
showFace = computed(() => store.state.components.chatBox?.showPlayerFaces && props.message.playerAccount),
showSender = computed(() => props.message.playerName && props.message.type === 'chat'), showSender = computed(() => props.message.playerName && props.message.type === 'chat'),
messageChannel = computed(() => props.message.type === 'chat' ? props.message.channel : undefined), messageChannel = computed(() => props.message.type === 'chat' ? props.message.channel : undefined),
messageContent = computed(() => { messageContent = computed(() => {
@ -64,15 +63,7 @@
} }
}) })
onMounted(() => {
if(showFace.value) {
getMinecraftHead(props.message.playerAccount as string, 'small')
.then((result) => image.value = result.src).catch(() => {});
}
});
return { return {
image,
showFace, showFace,
showSender, showSender,
messageChannel, messageChannel,

View File

@ -20,21 +20,21 @@
<label :for="`player-${player.name}`" <label :for="`player-${player.name}`"
:class="{'player': true, 'player--hidden' : !!player.hidden, 'player--other-world': otherWorld}" :title="title" :class="{'player': true, 'player--hidden' : !!player.hidden, 'player--other-world': otherWorld}" :title="title"
@click.prevent="onLabelClick"> @click.prevent="onLabelClick">
<img v-if="imagesEnabled" width="16" height="16" class="player__icon" :src="image" alt="" aria-hidden="true" /> <PlayerImage v-if="imagesEnabled" :player="player" width="16" height="16" class="player__icon" aria-hidden="true"></PlayerImage>
<span class="player__name" v-html="player.displayName"></span> <span class="player__name" v-html="player.displayName"></span>
</label> </label>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, computed, ref, onMounted} from 'vue'; import {defineComponent, computed} from 'vue';
import {useStore} from "@/store"; import {useStore} from "@/store";
import {MutationTypes} from "@/store/mutation-types"; import {MutationTypes} from "@/store/mutation-types";
import {getMinecraftHead} from '@/util';
import defaultImage from '@/assets/images/player_face.png';
import {LiveAtlasPlayer} from "@/index"; import {LiveAtlasPlayer} from "@/index";
import PlayerImage from "@/components/PlayerImage.vue";
export default defineComponent({ export default defineComponent({
name: 'PlayerListItem', name: 'PlayerListItem',
components: {PlayerImage},
props: { props: {
player: { player: {
type: Object as () => LiveAtlasPlayer, type: Object as () => LiveAtlasPlayer,
@ -44,7 +44,6 @@ export default defineComponent({
setup(props) { setup(props) {
const store = useStore(), const store = useStore(),
imagesEnabled = computed(() => store.state.components.players.showImages), imagesEnabled = computed(() => store.state.components.players.showImages),
image = ref(defaultImage),
otherWorld = computed(() => { otherWorld = computed(() => {
return store.state.components.players.grayHiddenPlayers return store.state.components.players.grayHiddenPlayers
@ -90,15 +89,8 @@ export default defineComponent({
} }
}; };
onMounted(() => {
if(imagesEnabled.value) {
getMinecraftHead(props.player, 'small').then((result) => image.value = result.src).catch(() => {});
}
});
return { return {
imagesEnabled, imagesEnabled,
image,
title, title,
otherWorld, otherWorld,
followTarget, followTarget,

View File

@ -19,7 +19,7 @@
<h2>{{ heading }}</h2> <h2>{{ heading }}</h2>
<div :class="{'following__target': true, 'following__target--hidden': target.hidden}"> <div :class="{'following__target': true, 'following__target--hidden': target.hidden}">
<img v-if="imagesEnabled" width="48" height="48" class="target__icon" :src="image" alt="" /> <PlayerImage v-if="imagesEnabled" :player="target" class="target__icon" width="48" height="48"></PlayerImage>
<span class="target__name" v-html="target.displayName"></span> <span class="target__name" v-html="target.displayName"></span>
<span class="target__status">{{ status }}</span> <span class="target__status">{{ status }}</span>
<span class="target__location" v-clipboard:copy="location" <span class="target__location" v-clipboard:copy="location"
@ -36,16 +36,16 @@
<script lang="ts"> <script lang="ts">
import {useStore} from "@/store"; import {useStore} from "@/store";
import {MutationTypes} from "@/store/mutation-types"; import {MutationTypes} from "@/store/mutation-types";
import {computed, defineComponent, onMounted, ref, watch} from "@vue/runtime-core"; import {computed, defineComponent} from "@vue/runtime-core";
import {clipboardError, clipboardSuccess, getMinecraftHead} from '@/util'; import {clipboardError, clipboardSuccess} from '@/util';
import defaultImage from '@/assets/images/player_face.png';
import {LiveAtlasPlayer} from "@/index"; import {LiveAtlasPlayer} from "@/index";
import SvgIcon from "@/components/SvgIcon.vue"; import SvgIcon from "@/components/SvgIcon.vue";
import "@/assets/icons/cross.svg"; import "@/assets/icons/cross.svg";
import PlayerImage from "@/components/PlayerImage.vue";
export default defineComponent({ export default defineComponent({
name: 'FollowTargetSection', name: 'FollowTargetSection',
components: {SvgIcon}, components: {PlayerImage, SvgIcon},
props: { props: {
target: { target: {
type: Object as () => LiveAtlasPlayer, type: Object as () => LiveAtlasPlayer,
@ -55,8 +55,6 @@ export default defineComponent({
setup(props) { setup(props) {
const store = useStore(), const store = useStore(),
imagesEnabled = computed(() => store.state.components.players.showImages), imagesEnabled = computed(() => store.state.components.players.showImages),
image = ref(defaultImage),
account = computed(() => props.target.name),
heading = computed(() => store.state.messages.followingHeading), heading = computed(() => store.state.messages.followingHeading),
messageUnfollow = computed(() => store.state.messages.followingUnfollow), messageUnfollow = computed(() => store.state.messages.followingUnfollow),
@ -82,25 +80,10 @@ export default defineComponent({
unfollow = () => { unfollow = () => {
store.commit(MutationTypes.CLEAR_FOLLOW_TARGET, undefined); store.commit(MutationTypes.CLEAR_FOLLOW_TARGET, undefined);
},
updatePlayerImage = async () => {
image.value = defaultImage;
if (imagesEnabled.value) {
try {
const result = await getMinecraftHead(props.target, 'small');
image.value = result.src;
} catch (e) {
}
}
}; };
watch(account, () => updatePlayerImage());
onMounted(() => updatePlayerImage());
return { return {
imagesEnabled, imagesEnabled,
image,
unfollow, unfollow,
heading, heading,
messageUnfollow, messageUnfollow,