FEATURE: migrated the chat window to svelte

This commit is contained in:
kharhamel 2021-07-07 18:07:58 +02:00
parent e5f7c62e25
commit 3cfbcc6b02
24 changed files with 470 additions and 305 deletions

View file

@ -0,0 +1,97 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import {chatMessagesStore, chatVisibilityStore} from "../../Stores/ChatStore";
import ChatMessageForm from './ChatMessageForm.svelte';
import ChatElement from './ChatElement.svelte';
import {onMount} from "svelte";
let listDom: HTMLElement;
onMount(() => {
listDom.addEventListener('onscroll', function(e: Event) {
console.log(e);
// Active list item is top-most fully-visible item
//const visibleListItems = Array.from(document.getElementsByClassName('list-item')).map(inView.is);
// Array.indexOf() will give us the first one in list, so the current active item
//const topMostVisible = visibleListItems.indexOf(true);
});
})
function closeChat() {
chatVisibilityStore.set(false);
}
function onKeyDown(e:KeyboardEvent) {
if (e.key === 'Escape') {
closeChat();
}
}
</script>
<svelte:window on:keydown={onKeyDown}/>
<aside class="chatWindow" transition:fly="{{ x: -1000, duration: 500 }}">
<section class="chatWindowTitle">
<h3>Here is your chat history <button on:click={closeChat}>❌</button></h3>
</section>
<section class="messagesList">
<ul bind:this={listDom}>
{#each $chatMessagesStore as message}
<li><ChatElement message={message}></ChatElement></li>
{/each}
</ul>
</section>
<section class="messageForm">
<ChatMessageForm></ChatMessageForm>
</section>
</aside>
<style lang="scss">
h3 {
font-family: 'Whiteney';
}
aside.chatWindow {
z-index:100;
pointer-events: auto;
position: absolute;
top: 0;
left: 0;
height: 100vh;
width:30vw;
min-width: 350px;
background: #051f33;
color: whitesmoke;
display: flex;
flex-direction: column;
padding: 10px;
border-bottom-right-radius: 16px;
border-top-right-radius: 16px;
h3 {
background-color: #5f5f5f;
border-radius: 8px;
padding: 2px;
font-size: 100%;
}
.chatWindowTitle {
flex: 0 100px;
}
.messagesList {
overflow-y: auto;
flex: auto;
ul {
list-style-type: none;
padding-left: 0;
}
}
.messageForm {
flex: 0 70px;
padding-top: 20px;
}
}
</style>

View file

@ -0,0 +1,74 @@
<script lang="ts">
import {ChatMessageTypes} from "../../Stores/ChatStore";
import type {ChatMessage} from "../../Stores/ChatStore";
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
import ChatPlayerName from './ChatPlayerName.svelte';
import type {PlayerInterface} from "../../Phaser/Game/PlayerInterface";
export let message: ChatMessage;
$: author = message.author as PlayerInterface;
$: targets = message.targets || [];
$: texts = message.text || [];
function urlifyText(text: string): string {
return HtmlUtils.urlify(text)
}
function renderDate(date: Date) {
return date.toLocaleTimeString(navigator.language, {
hour: '2-digit',
minute:'2-digit'
});
}
</script>
<div class="chatElement">
<div class="messagePart">
{#if message.type === ChatMessageTypes.userIncoming}
➡️: {#each targets as target}<ChatPlayerName player={target}></ChatPlayerName>{/each} ({renderDate(message.date)})
{:else if message.type === ChatMessageTypes.userOutcoming}
⬅️: {#each targets as target}<ChatPlayerName player={target}></ChatPlayerName>{/each} ({renderDate(message.date)})
{:else if message.type === ChatMessageTypes.me}
<h4>Me: ({renderDate(message.date)})</h4>
{#each texts as text}
<div><p class="my-text">{@html urlifyText(text)}</p></div>
{/each}
{:else}
<h4><ChatPlayerName player={author}></ChatPlayerName>: ({renderDate(message.date)})</h4>
{#each texts as text}
<div><p class="other-text">{@html urlifyText(text)}</p></div>
{/each}
{/if}
</div>
</div>
<style lang="scss">
h4, p {
font-family: 'Whiteney';
}
div.chatElement {
display: flex;
margin-bottom: 20px;
.messagePart {
flex-grow:1;
max-width: 100%;
div > p {
border-radius: 8px;
margin-bottom: 10px;
padding:6px;
overflow-wrap: break-word;
max-width: 100%;
display: inline-block;
&.other-text {
background: gray;
}
&.my-text {
background: #6489ff;
}
}
}
}
</style>

View file

@ -0,0 +1,55 @@
<script lang="ts">
import {chatMessagesStore, chatInputFocusStore} from "../../Stores/ChatStore";
let newMessageText = '';
function onFocus() {
chatInputFocusStore.set(true);
}
function onBlur() {
chatInputFocusStore.set(false);
}
function saveMessage() {
if (!newMessageText) return;
chatMessagesStore.addPersonnalMessage(newMessageText);
newMessageText = '';
}
</script>
<form on:submit|preventDefault={saveMessage}>
<input type="text" bind:value={newMessageText} placeholder="Type here" on:focus={onFocus} on:blur={onBlur} >
<button type="submit">
<img src="/static/images/send.png" alt="Send" width="20">
</button>
</form>
<style lang="scss">
form {
display: flex;
padding-left: 4px;
padding-right: 4px;
input {
flex: auto;
background-color: #42464d;
color: white;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
border: none;
font-size: 22px;
font-family: Whiteney;
}
button {
background-color: #42464d;
color: white;
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
border: none;
border-left: solid black 1px;
font-size: 16px;
font-family: Whiteney;
}
}
</style>

View file

@ -0,0 +1,37 @@
<script lang="ts">
import type {PlayerInterface} from "../../Phaser/Game/PlayerInterface";
import {requestVisitCardsStore} from "../../Stores/GameStore";
export let player:PlayerInterface;
let showMenu: boolean = false;
function openVisitCard() {
if (player.visitCardUrl) {
requestVisitCardsStore.set(player.visitCardUrl);
}
}
</script>
<span class="chatPlayerName" style="color: {player.color || 'white'}" on:click={() => showMenu = !showMenu}>
{player.name}
</span>
{#if showMenu}
<ul class="selectMenu">
<li><button class="text-btn" disabled={!player.visitCardUrl} on:click={openVisitCard}>Visit card</button></li>
</ul>
{/if}
<style lang="scss">
.chatPlayerName:hover {
text-decoration: underline;
cursor: pointer;
}
ul.selectMenu {
background-color: whitesmoke;
position: absolute;
right: 0;
border-radius: 4px;
}
</style>