Chat
This commit is contained in:
parent
53f5960e52
commit
cd22237e33
14
src/App.vue
14
src/App.vue
@ -17,11 +17,11 @@
|
||||
<template>
|
||||
<Map></Map>
|
||||
<Sidebar></Sidebar>
|
||||
<Chat v-if="false"></Chat>
|
||||
<Chat v-if="chatEnabled" v-show="chatEnabled && chatVisible"></Chat>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, computed, ref, onMounted, onBeforeUnmount, watch} from 'vue';
|
||||
import {computed, defineComponent, onBeforeUnmount, onMounted, onUnmounted, ref, watch} from 'vue';
|
||||
import Map from './components/Map.vue';
|
||||
import Sidebar from './components/Sidebar.vue';
|
||||
import Chat from './components/Chat.vue';
|
||||
@ -44,6 +44,7 @@ export default defineComponent({
|
||||
title = computed(() => store.state.configuration.title),
|
||||
currentUrl = computed(() => store.getters.url),
|
||||
chatEnabled = computed(() => store.state.components.chat),
|
||||
chatVisible = computed(() => store.state.ui.visibleElements.has('chat')),
|
||||
updatesEnabled = ref(false),
|
||||
updateTimeout = ref(0),
|
||||
configAttempts = ref(0),
|
||||
@ -101,6 +102,10 @@ export default defineComponent({
|
||||
|
||||
store.commit(MutationTypes.SET_PARSED_URL, parsedUrl);
|
||||
}
|
||||
},
|
||||
|
||||
onResize = () => {
|
||||
store.commit(MutationTypes.SET_SMALL_SCREEN, window.innerWidth < 480 || window.innerHeight < 500);
|
||||
};
|
||||
|
||||
watch(title, (title) => document.title = title);
|
||||
@ -110,9 +115,14 @@ export default defineComponent({
|
||||
onBeforeUnmount(() => stopUpdates());
|
||||
|
||||
parseUrl();
|
||||
onResize();
|
||||
|
||||
onMounted(() => window.addEventListener('resize', onResize));
|
||||
onUnmounted(() => window.addEventListener('resize', onResize));
|
||||
|
||||
return {
|
||||
chatEnabled,
|
||||
chatVisible,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
53
src/api.ts
53
src/api.ts
@ -126,6 +126,7 @@ function buildComponents(response: any): DynmapComponentConfig {
|
||||
markers: {
|
||||
showLabels: false,
|
||||
},
|
||||
chat: undefined,
|
||||
playerMarkers: undefined,
|
||||
coordinatesControl: undefined,
|
||||
linkControl: false,
|
||||
@ -188,6 +189,15 @@ function buildComponents(response: any): DynmapComponentConfig {
|
||||
position: component.position.replace('-', '') || 'topleft',
|
||||
image: component.logourl || undefined,
|
||||
});
|
||||
break;
|
||||
|
||||
case "chatbox":
|
||||
components.chat = {
|
||||
allowUrlName: component.allowurlname || false,
|
||||
showPlayerFaces: component.showplayerfaces || false,
|
||||
messageLifetime: component.messagettl || Infinity,
|
||||
messageHistory: component.scrollback || Infinity,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -444,13 +454,52 @@ function buildUpdates(data: Array<any>): DynmapUpdates {
|
||||
|
||||
updates.chat.push({
|
||||
type: 'chat',
|
||||
account: entry.account,
|
||||
message: entry.message,
|
||||
playerAccount: entry.account,
|
||||
playerName: entry.playerName ? sanitizer.sanitize(entry.playerName) : "",
|
||||
message: entry.message ? sanitizer.sanitize(entry.message) : "",
|
||||
timestamp: entry.timestamp,
|
||||
channel: entry.channel || undefined,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'playerjoin':
|
||||
if(!entry.account || !entry.timestamp) {
|
||||
dropped.incomplete++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(entry.timestamp < lastUpdate) {
|
||||
dropped.stale++;
|
||||
continue;
|
||||
}
|
||||
|
||||
updates.chat.push({
|
||||
type: 'playerjoin',
|
||||
playerAccount: entry.account,
|
||||
playerName: entry.playerName ? sanitizer.sanitize(entry.playerName) : "",
|
||||
timestamp: entry.timestamp || undefined,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'playerquit':
|
||||
if(!entry.account || !entry.timestamp) {
|
||||
dropped.incomplete++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(entry.timestamp < lastUpdate) {
|
||||
dropped.stale++;
|
||||
continue;
|
||||
}
|
||||
|
||||
updates.chat.push({
|
||||
type: 'playerleave',
|
||||
playerAccount: entry.account,
|
||||
playerName: entry.playerName ? sanitizer.sanitize(entry.playerName) : "",
|
||||
timestamp: entry.timestamp || undefined,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'tile':
|
||||
if(!entry.name || !entry.timestamp) {
|
||||
dropped.incomplete++;
|
||||
|
@ -17,7 +17,7 @@
|
||||
<template>
|
||||
<section class="chat">
|
||||
<ul class="chat__messages">
|
||||
<li class="message" v-for="message in chat" :key="message.timestamp">{{ message.message || 'aaaa' }}</li>
|
||||
<ChatMessage v-for="message in chat" :key="message.timestamp" :message="message"></ChatMessage>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
@ -25,8 +25,12 @@
|
||||
<script lang="ts">
|
||||
import {defineComponent, computed} from "@vue/runtime-core";
|
||||
import {useStore} from "@/store";
|
||||
import ChatMessage from "@/components/chat/ChatMessage.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ChatMessage
|
||||
},
|
||||
setup() {
|
||||
const store = useStore(),
|
||||
chat = computed(() => store.state.chat);
|
||||
@ -39,5 +43,47 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../scss/variables';
|
||||
@import '../scss/placeholders';
|
||||
|
||||
.chat {
|
||||
@extend %panel;
|
||||
position: absolute;
|
||||
bottom: 7rem;
|
||||
left: 7rem;
|
||||
width: 50rem;
|
||||
max-width: calc(100% - 8rem);
|
||||
max-height: 20rem;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
|
||||
.chat__messages {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
list-style: none;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.message {
|
||||
font-size: 1.6rem;
|
||||
|
||||
& + .message {
|
||||
margin-bottom: 0.5rem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 25rem), (max-height: 30rem) {
|
||||
bottom: 6.5rem;
|
||||
left: 6.5rem;
|
||||
max-width: calc(100% - 7rem);
|
||||
}
|
||||
|
||||
@media (max-width: 20rem) {
|
||||
.chat__messages .message + .message {
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -24,7 +24,7 @@
|
||||
<CoordinatesControl v-if="coordinatesControlEnabled" :leaflet="leaflet"></CoordinatesControl>
|
||||
<LinkControl v-if="linkControlEnabled" :leaflet="leaflet"></LinkControl>
|
||||
<ClockControl v-if="clockControlEnabled" :leaflet="leaflet"></ClockControl>
|
||||
<ChatControl v-if="false" :leaflet="leaflet"></ChatControl>
|
||||
<ChatControl v-if="chatEnabled" :leaflet="leaflet"></ChatControl>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
124
src/components/chat/ChatMessage.vue
Normal file
124
src/components/chat/ChatMessage.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<!--
|
||||
- Copyright 2020 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>
|
||||
<li :class="`message message--${message.type}`">
|
||||
<img v-if="facesEnabled" width="16" height="16" class="message__face" :src="image" alt="" />
|
||||
<span v-if="showSender" class="message__sender" v-html="message.playerName"></span>
|
||||
<span class="message__content" v-html="messageContent"></span>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, ref, onMounted, computed} from "@vue/runtime-core";
|
||||
import {DynmapChat} from "@/dynmap";
|
||||
import Util from '@/util';
|
||||
import {useStore} from "@/store";
|
||||
|
||||
const defaultImage = require('@/assets/images/player_face.png');
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
message: {
|
||||
type: Object as () => DynmapChat,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
let image = ref(defaultImage),
|
||||
facesEnabled = computed(() => store.state.components.chat?.showPlayerFaces),
|
||||
showSender = computed(() => props.message.type === 'chat'),
|
||||
messageContent = computed(() => {
|
||||
switch(props.message.type) {
|
||||
case 'chat':
|
||||
return props.message.message;
|
||||
case 'playerjoin':
|
||||
if(props.message.playerName) {
|
||||
return store.state.messages.playerJoin
|
||||
.replace('%playername%', props.message.playerName);
|
||||
} else {
|
||||
return store.state.messages.anonymousJoin;
|
||||
}
|
||||
case 'playerleave':
|
||||
if(props.message.playerName) {
|
||||
return store.state.messages.playerQuit
|
||||
.replace('%playername%', props.message.playerName);
|
||||
} else {
|
||||
return store.state.messages.anonymousQuit;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if(store.state.components.playerMarkers && store.state.components.playerMarkers.showSkinFaces) {
|
||||
Util.getMinecraftHead(props.message.playerAccount, '16')
|
||||
.then((result) => image.value = result.src).catch(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
image,
|
||||
facesEnabled,
|
||||
showSender,
|
||||
messageContent
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.message {
|
||||
.message__face {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.message__sender {
|
||||
margin-right: 0.5rem;
|
||||
word-wrap: break-word;
|
||||
|
||||
&:after {
|
||||
content: ': ';
|
||||
}
|
||||
}
|
||||
|
||||
.message__content {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&.message--join,
|
||||
&.message--leave {
|
||||
.message__sender:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 20rem) {
|
||||
&.message--chat {
|
||||
.message__sender:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.message__content {
|
||||
display: block;
|
||||
color: #eeeeee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
3
src/dynmap.d.ts
vendored
3
src/dynmap.d.ts
vendored
@ -298,7 +298,8 @@ interface DynmapParsedUrl {
|
||||
|
||||
interface DynmapChat {
|
||||
type: 'chat' | 'playerjoin' | 'playerleave';
|
||||
account: string;
|
||||
playerAccount: string;
|
||||
playerName: string;
|
||||
channel?: string;
|
||||
message?: string;
|
||||
// source?: string;
|
||||
|
@ -16,6 +16,9 @@
|
||||
|
||||
import {Control, ControlOptions, DomUtil, Map} from 'leaflet';
|
||||
import chat from '@/assets/icons/chat.svg';
|
||||
import {useStore} from "@/store";
|
||||
import {MutationTypes} from "@/store/mutation-types";
|
||||
import {watch} from "@vue/runtime-core";
|
||||
|
||||
export class ChatControl extends Control {
|
||||
// @ts-ignore
|
||||
@ -35,6 +38,16 @@ export class ChatControl extends Control {
|
||||
<use xlink:href="${chat.url}" />
|
||||
</svg>`;
|
||||
|
||||
chatButton.addEventListener('click', e => {
|
||||
useStore().commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'chat');
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
watch(useStore().state.ui.visibleElements, (newValue) => {
|
||||
chatButton.classList.toggle('active', newValue.has('chat'));
|
||||
});
|
||||
|
||||
return chatButton;
|
||||
}
|
||||
}
|
||||
|
@ -396,31 +396,7 @@ button {
|
||||
* Chat
|
||||
*/
|
||||
|
||||
.chat {
|
||||
@extend %panel;
|
||||
position: absolute;
|
||||
bottom: 7rem;
|
||||
left: 7rem;
|
||||
width: 50rem;
|
||||
max-width: calc(100% - 8rem);
|
||||
max-height: 20rem;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
|
||||
.chat__messages {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
list-style: none;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.message {
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: 0.5rem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chatinput {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user