mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 12:25:20 +00:00
refac: user status
This commit is contained in:
parent
b27243df81
commit
aef1e06f0b
5 changed files with 131 additions and 130 deletions
|
|
@ -413,7 +413,7 @@ input[type='number'] {
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
box-decoration-break: clone;
|
box-decoration-break: clone;
|
||||||
padding: 0.1rem 0.3rem;
|
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 {
|
.mention::after {
|
||||||
|
|
@ -424,7 +424,7 @@ input[type='number'] {
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
box-decoration-break: clone;
|
box-decoration-break: clone;
|
||||||
padding: 0.1rem 0.3rem;
|
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 {
|
.tiptap .suggestion::after {
|
||||||
|
|
|
||||||
|
|
@ -140,10 +140,7 @@
|
||||||
{#if showUserProfile}
|
{#if showUserProfile}
|
||||||
<ProfilePreview user={message.user}>
|
<ProfilePreview user={message.user}>
|
||||||
<ProfileImage
|
<ProfileImage
|
||||||
src={message.user?.profile_image_url ??
|
src={message.user?.profile_image_url ?? `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||||
($i18n.language === 'dg-DG'
|
|
||||||
? `${WEBUI_BASE_URL}/doge.png`
|
|
||||||
: `${WEBUI_BASE_URL}/static/favicon.png`)}
|
|
||||||
className={'size-8 translate-y-1 ml-0.5'}
|
className={'size-8 translate-y-1 ml-0.5'}
|
||||||
/>
|
/>
|
||||||
</ProfilePreview>
|
</ProfilePreview>
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,24 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { DropdownMenu } from 'bits-ui';
|
import { LinkPreview } from 'bits-ui';
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
import UserStatus from './UserStatus.svelte';
|
||||||
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';
|
|
||||||
|
|
||||||
export let user = null;
|
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>
|
</script>
|
||||||
|
|
||||||
<DropdownMenu.Root
|
<LinkPreview.Root openDelay={0} closeDelay={0}>
|
||||||
bind:open={show}
|
<LinkPreview.Trigger class=" cursor-pointer no-underline! font-normal! ">
|
||||||
closeFocus={false}
|
|
||||||
onOpenChange={(state) => {}}
|
|
||||||
typeahead={false}
|
|
||||||
>
|
|
||||||
<DropdownMenu.Trigger>
|
|
||||||
<slot />
|
<slot />
|
||||||
</DropdownMenu.Trigger>
|
</LinkPreview.Trigger>
|
||||||
|
|
||||||
<slot name="content">
|
<LinkPreview.Content
|
||||||
<DropdownMenu.Content
|
class="max-w-full w-[240px] rounded-2xl z-9999 bg-white dark:bg-black dark:text-white shadow-lg"
|
||||||
class="max-w-full w-[240px] rounded-lg z-9999 bg-white dark:bg-black dark:text-white shadow-lg"
|
side="left"
|
||||||
|
align="center"
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
{side}
|
|
||||||
{align}
|
|
||||||
transition={flyAndScale}
|
|
||||||
>
|
>
|
||||||
{#if user}
|
<UserStatus id={user.id} />
|
||||||
<div class=" flex flex-col gap-2 w-full rounded-lg">
|
</LinkPreview.Content>
|
||||||
<div class="py-8 relative bg-gray-900 rounded-t-lg">
|
</LinkPreview.Root>
|
||||||
<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>
|
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Token } from 'marked';
|
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 { goto } from '$app/navigation';
|
||||||
import { channels, models } from '$lib/stores';
|
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;
|
export let token: Token;
|
||||||
|
|
||||||
|
|
@ -61,10 +66,13 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Tooltip
|
<LinkPreview.Root openDelay={0} closeDelay={0}>
|
||||||
as="span"
|
<LinkPreview.Trigger class="mention cursor-pointer no-underline! font-normal! ">
|
||||||
className="mention cursor-pointer"
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
onClick={async () => {
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
|
||||||
|
<span
|
||||||
|
on:click={async () => {
|
||||||
if (triggerChar === '@') {
|
if (triggerChar === '@') {
|
||||||
if (idType === 'U') {
|
if (idType === 'U') {
|
||||||
// Open user profile
|
// Open user profile
|
||||||
|
|
@ -88,8 +96,20 @@
|
||||||
console.log('Clicked mention', id);
|
console.log('Clicked mention', id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
content={id}
|
>
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
{triggerChar}{label}
|
{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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue