refac: user status

This commit is contained in:
Timothy Jaeryang Baek 2025-09-16 22:17:35 -05:00
parent b27243df81
commit aef1e06f0b
5 changed files with 131 additions and 130 deletions

View file

@ -413,7 +413,7 @@ input[type='number'] {
border-radius: 0.4rem;
box-decoration-break: clone;
padding: 0.1rem 0.3rem;
@apply text-sky-700 dark:text-blue-300 bg-sky-300/15 dark:bg-sky-500/15;
@apply text-sky-800 dark:text-sky-200 bg-sky-300/15 dark:bg-sky-500/15;
}
.mention::after {
@ -424,7 +424,7 @@ input[type='number'] {
border-radius: 0.4rem;
box-decoration-break: clone;
padding: 0.1rem 0.3rem;
@apply text-sky-700 dark:text-blue-300 bg-sky-300/15 dark:bg-sky-500/15;
@apply text-sky-800 dark:text-sky-200 bg-sky-300/15 dark:bg-sky-500/15;
}
.tiptap .suggestion::after {

View file

@ -140,10 +140,7 @@
{#if showUserProfile}
<ProfilePreview user={message.user}>
<ProfileImage
src={message.user?.profile_image_url ??
($i18n.language === 'dg-DG'
? `${WEBUI_BASE_URL}/doge.png`
: `${WEBUI_BASE_URL}/static/favicon.png`)}
src={message.user?.profile_image_url ?? `${WEBUI_BASE_URL}/static/favicon.png`}
className={'size-8 translate-y-1 ml-0.5'}
/>
</ProfilePreview>

View file

@ -1,101 +1,24 @@
<script lang="ts">
import { DropdownMenu } from 'bits-ui';
import { LinkPreview } from 'bits-ui';
import { getContext } from 'svelte';
const i18n = getContext('i18n');
import { flyAndScale } from '$lib/utils/transitions';
import { WEBUI_BASE_URL } from '$lib/constants';
import { getUserActiveStatusById } from '$lib/apis/users';
export let side = 'right';
export let align = 'top';
import UserStatus from './UserStatus.svelte';
export let user = null;
let show = false;
let active = false;
const getActiveStatus = async () => {
const res = await getUserActiveStatusById(localStorage.token, user.id).catch((error) => {
console.error('Error fetching user active status:', error);
});
if (res) {
active = res.active;
} else {
active = false;
}
};
$: if (show) {
getActiveStatus();
}
</script>
<DropdownMenu.Root
bind:open={show}
closeFocus={false}
onOpenChange={(state) => {}}
typeahead={false}
>
<DropdownMenu.Trigger>
<LinkPreview.Root openDelay={0} closeDelay={0}>
<LinkPreview.Trigger class=" cursor-pointer no-underline! font-normal! ">
<slot />
</DropdownMenu.Trigger>
</LinkPreview.Trigger>
<slot name="content">
<DropdownMenu.Content
class="max-w-full w-[240px] rounded-lg z-9999 bg-white dark:bg-black dark:text-white shadow-lg"
<LinkPreview.Content
class="max-w-full w-[240px] rounded-2xl z-9999 bg-white dark:bg-black dark:text-white shadow-lg"
side="left"
align="center"
sideOffset={8}
{side}
{align}
transition={flyAndScale}
>
{#if user}
<div class=" flex flex-col gap-2 w-full rounded-lg">
<div class="py-8 relative bg-gray-900 rounded-t-lg">
<img
crossorigin="anonymous"
src={user?.profile_image_url ?? `${WEBUI_BASE_URL}/static/favicon.png`}
class=" absolute -bottom-5 left-3 size-12 ml-0.5 object-cover rounded-full -translate-y-[1px]"
alt="profile"
/>
</div>
<div class=" flex flex-col pt-4 pb-2.5 px-4">
<div class=" -mb-1">
<span class="font-medium text-sm line-clamp-1"> {user.name} </span>
</div>
<div class=" flex items-center gap-2">
{#if active}
<div>
<span class="relative flex size-2">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
/>
<span class="relative inline-flex rounded-full size-2 bg-green-500" />
</span>
</div>
<div class=" -translate-y-[1px]">
<span class="text-xs"> {$i18n.t('Active')} </span>
</div>
{:else}
<div>
<span class="relative flex size-2">
<span class="relative inline-flex rounded-full size-2 bg-gray-500" />
</span>
</div>
<div class=" -translate-y-[1px]">
<span class="text-xs"> {$i18n.t('Away')} </span>
</div>
{/if}
</div>
</div>
</div>
{/if}
</DropdownMenu.Content>
</slot>
</DropdownMenu.Root>
<UserStatus id={user.id} />
</LinkPreview.Content>
</LinkPreview.Root>

View file

@ -0,0 +1,61 @@
<script lang="ts">
import { getContext, onMount } from 'svelte';
const i18n = getContext('i18n');
import { WEBUI_BASE_URL } from '$lib/constants';
import { getUserActiveStatusById, getUserById } from '$lib/apis/users';
export let id = null;
let user = null;
onMount(async () => {
if (id) {
user = await getUserById(localStorage.token, id).catch((error) => {
console.error('Error fetching user by ID:', error);
return null;
});
}
});
</script>
{#if user}
<div class=" flex gap-3.5 w-full py-3 px-3 items-center">
<div class=" items-center flex shrink-0">
<img
crossorigin="anonymous"
src={user?.profile_image_url ?? `${WEBUI_BASE_URL}/static/favicon.png`}
class=" size-12 object-cover rounded-xl"
alt="profile"
/>
</div>
<div class=" flex flex-col w-full flex-1">
<div class="mb-0.5">
<span class="font-medium line-clamp-1"> {user.name} </span>
</div>
<div class=" flex items-center gap-2">
{#if user?.active}
<div>
<span class="relative flex size-2">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
/>
<span class="relative inline-flex rounded-full size-2 bg-green-500" />
</span>
</div>
<span class="text-xs"> {$i18n.t('Active')} </span>
{:else}
<div>
<span class="relative flex size-2">
<span class="relative inline-flex rounded-full size-2 bg-gray-500" />
</span>
</div>
<span class="text-xs"> {$i18n.t('Away')} </span>
{/if}
</div>
</div>
</div>
{/if}

View file

@ -1,9 +1,14 @@
<script lang="ts">
import type { Token } from 'marked';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import { LinkPreview } from 'bits-ui';
import { getContext } from 'svelte';
import { goto } from '$app/navigation';
import { channels, models } from '$lib/stores';
import i18n from '$lib/i18n';
import UserStatus from '$lib/components/channel/Messages/Message/UserStatus.svelte';
const i18n = getContext('i18n');
export let token: Token;
@ -61,10 +66,13 @@
};
</script>
<Tooltip
as="span"
className="mention cursor-pointer"
onClick={async () => {
<LinkPreview.Root openDelay={0} closeDelay={0}>
<LinkPreview.Trigger class="mention cursor-pointer no-underline! font-normal! ">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span
on:click={async () => {
if (triggerChar === '@') {
if (idType === 'U') {
// Open user profile
@ -88,8 +96,20 @@
console.log('Clicked mention', id);
}
}}
content={id}
placement="top"
>
>
{triggerChar}{label}
</Tooltip>
</span>
</LinkPreview.Trigger>
<LinkPreview.Content
class="max-w-full w-[240px] rounded-2xl z-9999 bg-white dark:bg-black dark:text-white shadow-lg"
side="top"
align="start"
sideOffset={6}
>
{#if triggerChar === '@' && idType === 'U'}
<UserStatus {id} />
{/if}
<!-- <div class="flex space-x-4">HI</div> -->
</LinkPreview.Content>
</LinkPreview.Root>