소스 검색

refac: styling

Timothy Jaeryang Baek 4 달 전
부모
커밋
a165e76486

+ 7 - 5
src/lib/components/channel/Channel.svelte

@@ -20,6 +20,10 @@
 		initHandler();
 	}
 
+	const scrollToBottom = () => {
+		messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
+	};
+
 	const initHandler = async () => {
 		top = false;
 		page = 1;
@@ -80,13 +84,11 @@
 
 <div class="h-full md:max-w-[calc(100%-260px)] w-full max-w-full flex flex-col">
 	<div
-		class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
+		class=" pb-2.5 max-w-full z-10 scrollbar-hidden w-full h-full pt-6 flex-1 flex flex-col-reverse overflow-auto"
 		id="messages-container"
 		bind:this={messagesContainerElement}
 		on:scroll={(e) => {
-			scrollEnd =
-				messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
-				messagesContainerElement.clientHeight + 5;
+			scrollEnd = Math.abs(messagesContainerElement.scrollTop) <= 50;
 		}}
 	>
 		{#key id}
@@ -110,6 +112,6 @@
 	</div>
 
 	<div class=" pb-[1rem]">
-		<MessageInput onSubmit={submitHandler} />
+		<MessageInput onSubmit={submitHandler} {scrollToBottom} {scrollEnd} />
 	</div>
 </div>

+ 33 - 0
src/lib/components/channel/MessageInput.svelte

@@ -18,6 +18,8 @@
 	let content = '';
 
 	export let onSubmit: Function;
+	export let scrollEnd = true;
+	export let scrollToBottom: Function;
 
 	let submitHandler = async () => {
 		if (content === '') {
@@ -36,6 +38,37 @@
 	};
 </script>
 
+<div class=" mx-auto inset-x-0 bg-transparent flex justify-center">
+	<div class="flex flex-col px-3 max-w-6xl w-full">
+		<div class="relative">
+			{#if scrollEnd === false}
+				<div class=" absolute -top-12 left-0 right-0 flex justify-center z-30 pointer-events-none">
+					<button
+						class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto"
+						on:click={() => {
+							scrollEnd = true;
+							scrollToBottom();
+						}}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 20 20"
+							fill="currentColor"
+							class="w-5 h-5"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</button>
+				</div>
+			{/if}
+		</div>
+	</div>
+</div>
+
 <div class="{transparentBackground ? 'bg-transparent' : 'bg-white dark:bg-gray-900'} ">
 	<div class="max-w-6xl px-2.5 mx-auto inset-x-0">
 		<div class="">

+ 27 - 24
src/lib/components/channel/Messages.svelte

@@ -9,7 +9,6 @@
 	import Message from './Messages/Message.svelte';
 	import Loader from '../common/Loader.svelte';
 	import Spinner from '../common/Spinner.svelte';
-	import { getChannelMessages } from '$lib/apis/channels';
 
 	const i18n = getContext('i18n');
 
@@ -35,28 +34,32 @@
 </script>
 
 {#if messages}
-	<div class="w-full h-full pt-2 flex-1 flex flex-col-reverse overflow-auto">
-		<div>
-			{#if !top}
-				<Loader
-					on:visible={(e) => {
-						console.log('visible');
-						if (!messagesLoading) {
-							loadMoreMessages();
-						}
-					}}
-				>
-					<div class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2">
-						<Spinner className=" size-4" />
-						<div class=" ">Loading...</div>
-					</div>
-				</Loader>
-			{/if}
-
-			{#each messages.slice().reverse() as message, messageIdx (message.id)}
-				<Message {message} />
-			{/each}
-		</div>
+	{@const messageList = messages.slice().reverse()}
+	<div>
+		{#if !top}
+			<Loader
+				on:visible={(e) => {
+					console.log('visible');
+					if (!messagesLoading) {
+						loadMoreMessages();
+					}
+				}}
+			>
+				<div class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2">
+					<Spinner className=" size-4" />
+					<div class=" ">Loading...</div>
+				</div>
+			</Loader>
+		{/if}
+
+		{#each messageList as message, messageIdx (message.id)}
+			<Message
+				{message}
+				showUserProfile={messageIdx === 0 ||
+					messageList.at(messageIdx + 1)?.user_id !== message.user_id}
+			/>
+		{/each}
+
+		<div class="pb-6" />
 	</div>
-	<div class="pb-6" />
 {/if}

+ 77 - 3
src/lib/components/channel/Messages/Message.svelte

@@ -1,13 +1,87 @@
 <script lang="ts">
+	import dayjs from 'dayjs';
+	import relativeTime from 'dayjs/plugin/relativeTime';
+	import isToday from 'dayjs/plugin/isToday';
+	import isYesterday from 'dayjs/plugin/isYesterday';
+
+	dayjs.extend(relativeTime);
+	dayjs.extend(isToday);
+	dayjs.extend(isYesterday);
+
+	import { getContext } from 'svelte';
+	const i18n = getContext<Writable<i18nType>>('i18n');
+
+	import { settings } from '$lib/stores';
+
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
 	import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
+	import ProfileImage from '$lib/components/chat/Messages/ProfileImage.svelte';
+	import Name from '$lib/components/chat/Messages/Name.svelte';
 
 	export let message;
+	export let showUserProfile = true;
+
+	const formatDate = (inputDate) => {
+		const date = dayjs(inputDate);
+		const now = dayjs();
+
+		if (date.isToday()) {
+			return `Today at ${date.format('HH:mm')}`;
+		} else if (date.isYesterday()) {
+			return `Yesterday at ${date.format('HH:mm')}`;
+		} else {
+			return `${date.format('DD/MM/YYYY')} at ${date.format('HH:mm')}`;
+		}
+	};
 </script>
 
 {#if message}
-	<div>
-		<div>
-			<Markdown id={message.id} content={message.content} />
+	<div
+		class="flex flex-col justify-between px-5 {showUserProfile
+			? 'mt-3'
+			: ''} w-full {($settings?.widescreenMode ?? null)
+			? 'max-w-full'
+			: 'max-w-5xl'} mx-auto rounded-lg group"
+	>
+		<div
+			class=" flex w-full message-{message.id}"
+			id="message-{message.id}"
+			dir={$settings.chatDirection}
+		>
+			<div
+				class={`flex-shrink-0 ${($settings?.chatDirection ?? 'LTR') === 'LTR' ? 'mr-3' : 'ml-3'}`}
+			>
+				{#if showUserProfile}
+					<ProfileImage
+						src={message.user?.profile_image_url ??
+							($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
+						className={'size-7'}
+					/>
+				{:else}
+					<div class="w-7 h-7 rounded-full bg-transparent" />
+				{/if}
+			</div>
+
+			<div class="flex-auto w-0 pl-1">
+				{#if showUserProfile}
+					<Name>
+						{message?.user?.name}
+
+						{#if message.created_at}
+							<span
+								class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium capitalize ml-0.5 -mt-0.5"
+							>
+								{formatDate(message.created_at / 1000000)}
+							</span>
+						{/if}
+					</Name>
+				{/if}
+
+				<div class="markdown-prose">
+					<Markdown id={message.id} content={message.content} />
+				</div>
+			</div>
 		</div>
 	</div>
 {/if}