Implemented chat

This commit is contained in:
thepaperpilot 2022-09-06 08:02:41 -05:00
parent 5051bd27d2
commit 7060349936
3 changed files with 148 additions and 1 deletions

139
src/data/Chat.vue Normal file
View file

@ -0,0 +1,139 @@
<template>
<div class="chat" :class="{ open }" v-show="room">
<div class="chat-toggle" @click="open = !open">
<span>Chat</span>
<span v-if="unread" style="margin-left: 10px">[{{ unread > 9 ? "9+" : unread }}]</span>
</div>
<div class="chat-messages" ref="scroll" @scroll.passive="onScroll">
<div v-for="(message, i) in messages" :key="i" class="chat-message-container">
<span class="chat-user" v-if="message.user">{{ nicknames[message.user] }}</span>
<span class="chat-message" :style="message.user ? '' : 'font-style: italic'">{{
message.message
}}</span>
</div>
</div>
<hr style="margin-top: 0" />
<div class="chat-submit">
<Text v-model="message" @submit="submit" :submitOnBlur="false" />
<button @pointerdown="submit" class="button">
<span class="material-icons">check</span>
</button>
</div>
</div>
</template>
<script setup lang="ts">
import Text from "components/fields/Text.vue";
import { globalBus } from "game/events";
import { nextTick, reactive, ref, watch } from "vue";
import { emit, nicknames, room } from "./socket";
const open = ref<boolean>(false);
const unread = ref<number>(0);
const message = ref("");
const messages = reactive<{ user?: string; message: string }[]>([]);
const scroll = ref<HTMLElement | null>(null);
function submit() {
emit("chat", message.value);
message.value = "";
}
globalBus.on("chat", (user, message) => {
messages.push({ user, message });
const atBottom =
scroll.value &&
scroll.value.scrollTop >= scroll.value.scrollHeight - scroll.value.clientHeight;
if (atBottom) {
nextTick(() => {
if (scroll.value) {
scroll.value.scrollTop = scroll.value.scrollHeight - scroll.value.clientHeight;
}
});
}
if ((!atBottom || !open.value) && user) {
unread.value++;
}
});
function onScroll() {
nextTick(() => {
if (
scroll.value &&
unread.value > 0 &&
scroll.value.scrollTop >= scroll.value.scrollHeight - scroll.value.clientHeight
) {
unread.value = 0;
}
});
}
watch(open, open => {
if (open) {
unread.value = 0;
}
});
</script>
<style scoped>
.chat {
position: fixed;
top: 100%;
right: 4px;
height: 500px;
width: 300px;
border: solid 1px var(--outline);
display: flex;
flex-flow: column;
background: var(--background);
}
.chat.open {
top: calc(100% - 500px);
}
.chat-toggle {
margin-top: -23px;
margin-left: -1px;
margin-right: -1px;
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
border: solid 1px var(--outline);
cursor: pointer;
user-select: none;
background: var(--background);
}
.chat-messages {
flex-grow: 1;
width: calc(100% - 8px);
padding: 4px;
word-break: break-all;
overflow-y: auto;
}
.chat-message-container {
font-size: smaller;
text-align: left;
margin-bottom: 4px;
}
.chat-user {
font-weight: bolder;
margin-right: 10px;
}
.chat-message {
font-weight: lighter;
}
.chat-submit {
display: flex;
width: 100%;
}
.chat-submit > form {
margin-left: 10px;
}
</style>

View file

@ -4,6 +4,8 @@ import { createLayer } from "game/layers";
import { persistent } from "game/persistence"; import { persistent } from "game/persistence";
import type { PlayerData } from "game/player"; import type { PlayerData } from "game/player";
import { computed } from "vue"; import { computed } from "vue";
import Chat from "./Chat.vue";
import { room } from "./socket";
/** /**
* @hidden * @hidden
@ -18,6 +20,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
display: jsx(() => ( display: jsx(() => (
<> <>
<div>placeholder</div> <div>placeholder</div>
<Chat />
</> </>
)) ))
}; };

View file

@ -1,7 +1,7 @@
import Text from "components/fields/Text.vue"; import Text from "components/fields/Text.vue";
import { jsx, setDefault } from "features/feature"; import { jsx, setDefault } from "features/feature";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
import settings, { registerSettingField } from "game/settings"; import { registerSettingField } from "game/settings";
import { io, Socket } from "socket.io-client"; import { io, Socket } from "socket.io-client";
import { load } from "util/save"; import { load } from "util/save";
import { ref, watch } from "vue"; import { ref, watch } from "vue";
@ -136,12 +136,16 @@ function setupSocket(socket: Socket<ServerToClientEvents, ClientToServerEvents>)
toast.info(message); toast.info(message);
globalBus.emit("serverSentInfo"); globalBus.emit("serverSentInfo");
}); });
socket.on("chat", (user, message) => {
globalBus.emit("chat", user, message);
});
socket.on("set rooms", rooms => { socket.on("set rooms", rooms => {
globalBus.emit("setRooms", rooms); globalBus.emit("setRooms", rooms);
}); });
socket.on("joined room", (r, hosting) => { socket.on("joined room", (r, hosting) => {
room.value = r; room.value = r;
isHosting.value = hosting; isHosting.value = hosting;
globalBus.emit("chat", undefined, "you joined " + r);
}); });
socket.on("left room", () => { socket.on("left room", () => {
room.value = null; room.value = null;
@ -186,5 +190,6 @@ declare module "game/events" {
openMultiplayer: VoidFunction; openMultiplayer: VoidFunction;
setRooms: (rooms: ClientRoomData[]) => void; setRooms: (rooms: ClientRoomData[]) => void;
serverSentInfo: VoidFunction; serverSentInfo: VoidFunction;
chat: (user: string | undefined, message: string) => void;
} }
} }