feat: implement whisper, (double)click on avatar
Users can insert a mention by clicking, and initiate a whisper by double-clicking on the avatar on a user's chat message
This commit is contained in:
		@@ -1,6 +1,10 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 import type { Message } from "fedwave-chat-client";
 | 
					 import type { Message } from "fedwave-chat-client";
 | 
				
			||||||
 import { room, username } from "./chat";
 | 
					 import { room } from "./chat";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 import { createEventDispatcher } from "svelte";
 | 
				
			||||||
 | 
					 const dispatch = createEventDispatcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 export let globalMode = false;
 | 
					 export let globalMode = false;
 | 
				
			||||||
 export let message: Message;
 | 
					 export let message: Message;
 | 
				
			||||||
 export let isHeader: boolean = true;
 | 
					 export let isHeader: boolean = true;
 | 
				
			||||||
@@ -8,7 +12,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<div class="message d-flex p-relative pl-8 pr-24" class:subseq="{!isHeader}">
 | 
					<div class="message d-flex p-relative pl-8 pr-24" class:subseq="{!isHeader}">
 | 
				
			||||||
    {#if isHeader}
 | 
					    {#if isHeader}
 | 
				
			||||||
	<div class="avatar radius-pill overflow-hidden no-select" style="background-color: {message.color}">
 | 
						<!-- TODO: Use <Avatar/> here	 -->
 | 
				
			||||||
 | 
						<div class="avatar radius-pill overflow-hidden no-select clickable"
 | 
				
			||||||
 | 
						     on:click="{() => dispatch('avatar:click', message)}"
 | 
				
			||||||
 | 
						     on:dblclick="{() => dispatch('avatar:dblclick', message)}"
 | 
				
			||||||
 | 
						     style="background-color: {message.color}">
 | 
				
			||||||
	    <img class="w-100" alt="{message.username}'s avatar" src="{message.avatar || '/troll_haz2.png'}">
 | 
						    <img class="w-100" alt="{message.username}'s avatar" src="{message.avatar || '/troll_haz2.png'}">
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
    {/if}
 | 
					    {/if}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,8 @@
 | 
				
			|||||||
  let viewport: Element, contents: Element;
 | 
					  let viewport: Element, contents: Element;
 | 
				
			||||||
  let tweet: HTMLAudioElement;
 | 
					  let tweet: HTMLAudioElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let input: string = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  shouldTweet.subscribe((should) => {
 | 
					  shouldTweet.subscribe((should) => {
 | 
				
			||||||
    if (should) {
 | 
					    if (should) {
 | 
				
			||||||
      if (tweet) {
 | 
					      if (tweet) {
 | 
				
			||||||
@@ -91,6 +93,10 @@
 | 
				
			|||||||
    bind:viewport
 | 
					    bind:viewport
 | 
				
			||||||
    bind:contents
 | 
					    bind:contents
 | 
				
			||||||
    on:scroll={onScroll}
 | 
					    on:scroll={onScroll}
 | 
				
			||||||
 | 
					    on:avatar:click={(e) => (
 | 
				
			||||||
 | 
					      (input += ` @${e.detail.username}#${e.detail.unum} `), (input = input.trim())
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    on:avatar:dblclick={(e) => (input = `/w ${e.detail.username}#${e.detail.unum} `)}
 | 
				
			||||||
  />
 | 
					  />
 | 
				
			||||||
  {#if !stickToBottom}
 | 
					  {#if !stickToBottom}
 | 
				
			||||||
    <FAB --background={'var(--base-700)'} on:click={() => scrollToBottom()}
 | 
					    <FAB --background={'var(--base-700)'} on:click={() => scrollToBottom()}
 | 
				
			||||||
@@ -98,7 +104,7 @@
 | 
				
			|||||||
    >
 | 
					    >
 | 
				
			||||||
  {/if}
 | 
					  {/if}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<Input />
 | 
					<Input bind:input />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
  .scroller {
 | 
					  .scroller {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,20 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 import { tick } from "svelte"
 | 
					 import { tick } from "svelte"
 | 
				
			||||||
 import { send } from "./chat";
 | 
					 import { send, whisper } from "./chat";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 let input = "";
 | 
					 export let input = "";
 | 
				
			||||||
 async function keydown(e: KeyboardEvent) {
 | 
					 async function keydown(e: KeyboardEvent) {
 | 
				
			||||||
     if(e.key === "Enter") {
 | 
					     if(e.key === "Enter") {
 | 
				
			||||||
	 send(input);
 | 
						 console.log(e);
 | 
				
			||||||
 | 
						 const tokens = input.trim().split(" ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 if(tokens[0] === '/w') {
 | 
				
			||||||
 | 
						     const who = tokens[1];
 | 
				
			||||||
 | 
						     const m = tokens.slice(2).join(' ');
 | 
				
			||||||
 | 
						     whisper(who, m);
 | 
				
			||||||
 | 
						 } else {
 | 
				
			||||||
 | 
						     send(input);
 | 
				
			||||||
 | 
						 }
 | 
				
			||||||
	 e.preventDefault();
 | 
						 e.preventDefault();
 | 
				
			||||||
	 await tick();
 | 
						 await tick();
 | 
				
			||||||
	 input = "";
 | 
						 input = "";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,10 @@
 | 
				
			|||||||
<div class="viewport" bind:this={viewport} on:scroll>
 | 
					<div class="viewport" bind:this={viewport} on:scroll>
 | 
				
			||||||
    <div class="contents" bind:this={contents}>
 | 
					    <div class="contents" bind:this={contents}>
 | 
				
			||||||
	{#each messages as message}
 | 
						{#each messages as message}
 | 
				
			||||||
            <ChatMessage message={message.m} isHeader={message.isHeader} globalMode={showChannel} />
 | 
					            <ChatMessage message={message.m}
 | 
				
			||||||
 | 
								 isHeader={message.isHeader}
 | 
				
			||||||
 | 
								 globalMode={showChannel}
 | 
				
			||||||
 | 
								 on:avatar:click on:avatar:dblclick />
 | 
				
			||||||
	{/each}
 | 
						{/each}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,10 @@ export function send(m: string | OutgoingMessage) {
 | 
				
			|||||||
  chat.sendMessage(m);
 | 
					  chat.sendMessage(m);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function whisper(who: string, m: string) {
 | 
				
			||||||
 | 
					  chat.sendWhisper(who, m);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let setConnected: Subscriber<boolean> | undefined;
 | 
					let setConnected: Subscriber<boolean> | undefined;
 | 
				
			||||||
export const connected = readable<boolean>(false, (setf) => {
 | 
					export const connected = readable<boolean>(false, (setf) => {
 | 
				
			||||||
  setConnected = setf;
 | 
					  setConnected = setf;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,10 @@
 | 
				
			|||||||
    flex-grow: 0;
 | 
					    flex-grow: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.shrink-0 {
 | 
				
			||||||
 | 
					    flex-shrink: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.basis-0 {
 | 
					.basis-0 {
 | 
				
			||||||
    flex-basis: 0;
 | 
					    flex-basis: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -62,8 +66,10 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.d-flex {
 | 
					@each $i in (block, inline, inline-block, flex, grid, contents) {
 | 
				
			||||||
    display: flex;
 | 
					    .d-#{$i} {
 | 
				
			||||||
 | 
						display: #{$i};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.col {
 | 
					.col {
 | 
				
			||||||
@@ -82,9 +88,16 @@
 | 
				
			|||||||
    width: 100vw;
 | 
					    width: 100vw;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mw-0 { min-width: 0 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.w-100 { width: 100%; }
 | 
					.w-100 { width: 100%; }
 | 
				
			||||||
.h-100 { height: 100%; }
 | 
					.h-100 { height: 100%; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ellipsis {
 | 
				
			||||||
 | 
					    text-overflow: ellipsis;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.hide-scrollbar {
 | 
					.hide-scrollbar {
 | 
				
			||||||
    /* hide scrollbar */
 | 
					    /* hide scrollbar */
 | 
				
			||||||
    -ms-overflow-style: none !important;
 | 
					    -ms-overflow-style: none !important;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user