mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-12 03:48:33 +00:00
refac
This commit is contained in:
parent
78613d86f7
commit
a165c83858
4 changed files with 145 additions and 119 deletions
|
|
@ -12,18 +12,18 @@
|
|||
<script lang="ts">
|
||||
import { LinkPreview } from 'bits-ui';
|
||||
import { getContext, onDestroy } from 'svelte';
|
||||
import { user as sessionUser } from '$lib/stores';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
import UserStatus from './UserStatus.svelte';
|
||||
const i18n = getContext<typeof import('$lib/i18n').default>('i18n');
|
||||
import UserStatusLinkPreview from './UserStatusLinkPreview.svelte';
|
||||
|
||||
export let user = null;
|
||||
export let user: { id?: string | null; name?: string | null } | null = null;
|
||||
|
||||
export let align = 'center';
|
||||
export let side = 'right';
|
||||
export let sideOffset = 8;
|
||||
|
||||
let openPreview = false;
|
||||
export let openPreview = false;
|
||||
|
||||
const closeProfilePreview = () => {
|
||||
if (openPreview) {
|
||||
|
|
@ -43,18 +43,25 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<LinkPreview.Root openDelay={0} closeDelay={200} bind:open={openPreview}>
|
||||
<LinkPreview.Trigger class="flex shrink-0 items-center">
|
||||
<button
|
||||
type="button"
|
||||
class=" cursor-pointer no-underline! font-normal!"
|
||||
on:click={() => {
|
||||
openPreview = !openPreview;
|
||||
}}
|
||||
{#if user?.id && $sessionUser}
|
||||
<LinkPreview.Root openDelay={0} closeDelay={200} bind:open={openPreview}>
|
||||
<LinkPreview.Trigger
|
||||
class="flex shrink-0 items-center cursor-pointer no-underline! font-normal!"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</LinkPreview.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<button
|
||||
{...props}
|
||||
type="button"
|
||||
aria-label={user.name || $i18n.t('User')}
|
||||
onclick={() => (openPreview = !openPreview)}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
{/snippet}
|
||||
</LinkPreview.Trigger>
|
||||
|
||||
<UserStatusLinkPreview id={user?.id} {openPreview} {side} {align} {sideOffset} />
|
||||
</LinkPreview.Root>
|
||||
<UserStatusLinkPreview id={user.id} {openPreview} {side} {align} {sideOffset} />
|
||||
</LinkPreview.Root>
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import Name from './Name.svelte';
|
||||
import ProfileImage from './ProfileImage.svelte';
|
||||
import ProfilePreview from '$lib/components/channel/Messages/Message/ProfilePreview.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import FileItem from '$lib/components/common/FileItem.svelte';
|
||||
import Markdown from './Markdown.svelte';
|
||||
|
|
@ -158,12 +159,14 @@
|
|||
<!-- LICENSE covers this Open WebUI fallback logo.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<ProfileImage
|
||||
src={authorId
|
||||
? `${WEBUI_API_BASE_URL}/users/${authorId}/profile/image`
|
||||
: `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
className={'size-7 user-message-profile-image'}
|
||||
/>
|
||||
<ProfilePreview user={{ id: authorId, name: authorName }}>
|
||||
<ProfileImage
|
||||
src={authorId
|
||||
? `${WEBUI_API_BASE_URL}/users/${authorId}/profile/image`
|
||||
: `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
className={'size-7 user-message-profile-image'}
|
||||
/>
|
||||
</ProfilePreview>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
import ChannelModal from './ChannelModal.svelte';
|
||||
import ProfilePreview from '$lib/components/channel/Messages/Message/ProfilePreview.svelte';
|
||||
import Emoji from '$lib/components/common/Emoji.svelte';
|
||||
import HashtagIcon from '$lib/components/icons/Hashtag.svelte';
|
||||
import LockIcon from '$lib/components/icons/Lock.svelte';
|
||||
|
|
@ -87,6 +88,41 @@
|
|||
? 'font-normal dark:text-white text-black'
|
||||
: ' dark:text-gray-400 text-gray-600'} cursor-pointer select-none"
|
||||
>
|
||||
{#if channel?.type === 'dm' && channel?.users}
|
||||
{@const channelMembers = channel.users.filter((u) => u.id !== $user?.id)}
|
||||
<div class="self-center mr-1">
|
||||
<div class="flex ml-[0.0625rem] mr-0.5 relative">
|
||||
{#each channelMembers.slice(0, 2) as u, index}
|
||||
<div class={index === 1 ? '-ml-2.5' : ''}>
|
||||
<ProfilePreview user={u}>
|
||||
<img
|
||||
src={`${WEBUI_API_BASE_URL}/users/${u.id}/profile/image`}
|
||||
alt={u.name}
|
||||
class="size-5.5 rounded-full border-2 border-white dark:border-gray-900"
|
||||
/>
|
||||
</ProfilePreview>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if channelMembers.length === 1}
|
||||
<div class="absolute bottom-0 right-0 pointer-events-none">
|
||||
<span class="relative flex size-2">
|
||||
{#if channelMembers[0]?.is_active}
|
||||
<span
|
||||
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"
|
||||
></span>
|
||||
{/if}
|
||||
<span
|
||||
class="relative inline-flex size-2 rounded-full {channelMembers[0]?.is_active
|
||||
? 'bg-green-500'
|
||||
: 'bg-gray-300 dark:bg-gray-700'} border-[1.5px] border-white dark:border-gray-900"
|
||||
></span>
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<a
|
||||
class="min-w-0 flex flex-1"
|
||||
href="/channels/{channel.id}"
|
||||
|
|
@ -111,52 +147,19 @@
|
|||
draggable="false"
|
||||
>
|
||||
<div class="flex min-w-0 flex-1 items-center gap-1">
|
||||
<div>
|
||||
{#if channel?.type === 'dm'}
|
||||
{#if channel?.users}
|
||||
{@const channelMembers = channel.users.filter((u) => u.id !== $user?.id)}
|
||||
<div class="flex ml-[0.0625rem] mr-0.5 relative">
|
||||
{#each channelMembers.slice(0, 2) as u, index}
|
||||
<img
|
||||
src={`${WEBUI_API_BASE_URL}/users/${u.id}/profile/image`}
|
||||
alt={u.name}
|
||||
class=" size-5.5 rounded-full border-2 border-white dark:border-gray-900 {index ===
|
||||
1
|
||||
? '-ml-2.5'
|
||||
: ''}"
|
||||
/>
|
||||
{/each}
|
||||
|
||||
{#if channelMembers.length === 1}
|
||||
<div class="absolute bottom-0 right-0">
|
||||
<span class="relative flex size-2">
|
||||
{#if channelMembers[0]?.is_active}
|
||||
<span
|
||||
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"
|
||||
></span>
|
||||
{/if}
|
||||
<span
|
||||
class="relative inline-flex size-2 rounded-full {channelMembers[0]?.is_active
|
||||
? 'bg-green-500'
|
||||
: 'bg-gray-300 dark:bg-gray-700'} border-[1.5px] border-white dark:border-gray-900"
|
||||
></span>
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<UserIcon className="size-4 ml-1 mr-0.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
{:else}
|
||||
<div class=" size-4 justify-center flex items-center ml-1">
|
||||
{#if isPublicChannel(channel)}
|
||||
<HashtagIcon className="size-3" strokeWidth="1.8" />
|
||||
{:else}
|
||||
<LockIcon className="size-3.5" strokeWidth="1.7" />
|
||||
{/if}
|
||||
</div>
|
||||
{#if channel?.type === 'dm'}
|
||||
{#if !channel?.users}
|
||||
<UserIcon className="size-4 ml-1 mr-0.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="size-4 shrink-0 justify-center flex items-center ml-1">
|
||||
{#if isPublicChannel(channel)}
|
||||
<HashtagIcon className="size-3" strokeWidth="1.8" />
|
||||
{:else}
|
||||
<LockIcon className="size-3.5" strokeWidth="1.7" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="text-left self-center min-w-0 flex-1 pr-1 flex items-center gap-1.5">
|
||||
{#if channel?.name}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
import DragGhost from '$lib/components/common/DragGhost.svelte';
|
||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||
import ChatHoverPreview from './ChatHoverPreview.svelte';
|
||||
import ProfilePreview from '$lib/components/channel/Messages/Message/ProfilePreview.svelte';
|
||||
import ChatIcon from './icons/Chat.svelte';
|
||||
import MoreHorizontalIcon from './icons/MoreHorizontal.svelte';
|
||||
import SparklesIcon from './icons/Sparkles.svelte';
|
||||
|
|
@ -107,6 +108,9 @@
|
|||
let focusWithin = false;
|
||||
let menuOpen = false;
|
||||
let openPreview = false;
|
||||
let openProfilePreview = false;
|
||||
|
||||
$: if (openProfilePreview) openPreview = false;
|
||||
|
||||
const closeHoverPreview = () => {
|
||||
if (openPreview) {
|
||||
|
|
@ -514,21 +518,6 @@
|
|||
</script>
|
||||
|
||||
{#snippet chatItemContent()}
|
||||
{#if ownerUserId}
|
||||
<Tooltip content={ownerName || 'Unknown'} className="flex items-center self-center shrink-0">
|
||||
<img
|
||||
src="{WEBUI_API_BASE_URL}/users/{ownerUserId}/profile/image"
|
||||
alt=""
|
||||
class="size-3.5 rounded-full shrink-0 object-cover mr-2"
|
||||
on:error={(e) => {
|
||||
if (!e.currentTarget.src.endsWith('/static/favicon.png')) {
|
||||
e.currentTarget.src = `${WEBUI_BASE_URL}/static/favicon.png`;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
<!-- Loading spinner for active chat (left side) -->
|
||||
{#if active}
|
||||
<div class="shrink-0 self-center pr-2">
|
||||
|
|
@ -645,44 +634,68 @@
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else if $mobile}
|
||||
<a
|
||||
id="sidebar-chat-item"
|
||||
class={chatItemClass}
|
||||
href="/c/{id}"
|
||||
aria-current={id === $chatId ? 'page' : undefined}
|
||||
on:click={selectChatHandler}
|
||||
draggable="false"
|
||||
>
|
||||
{@render chatItemContent()}
|
||||
</a>
|
||||
{:else}
|
||||
<LinkPreview.Root
|
||||
openDelay={300}
|
||||
closeDelay={0}
|
||||
disabled={confirmEdit || dragged || !($settings?.chatHoverPreview ?? true)}
|
||||
bind:open={openPreview}
|
||||
>
|
||||
<LinkPreview.Trigger
|
||||
id="sidebar-chat-item"
|
||||
class={chatItemClass}
|
||||
href="/c/{id}"
|
||||
aria-current={id === $chatId ? 'page' : undefined}
|
||||
onclick={selectChatHandler}
|
||||
ondblclick={renameChatFromDoubleClick}
|
||||
draggable="false"
|
||||
>
|
||||
{@render chatItemContent()}
|
||||
</LinkPreview.Trigger>
|
||||
<div id="sidebar-chat-item" class={chatItemClass}>
|
||||
{#if ownerUserId}
|
||||
<div class="flex items-center self-center shrink-0 mr-2">
|
||||
<ProfilePreview
|
||||
user={{ id: ownerUserId, name: ownerName }}
|
||||
bind:openPreview={openProfilePreview}
|
||||
>
|
||||
<img
|
||||
src="{WEBUI_API_BASE_URL}/users/{ownerUserId}/profile/image"
|
||||
alt=""
|
||||
class="size-3.5 rounded-full shrink-0 object-cover"
|
||||
on:error={(e) => {
|
||||
if (!e.currentTarget.src.endsWith('/static/favicon.png')) {
|
||||
e.currentTarget.src = `${WEBUI_BASE_URL}/static/favicon.png`;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ProfilePreview>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $mobile}
|
||||
<a
|
||||
class="flex min-w-0 flex-1 items-center"
|
||||
href="/c/{id}"
|
||||
aria-current={id === $chatId ? 'page' : undefined}
|
||||
on:click={selectChatHandler}
|
||||
draggable="false"
|
||||
>
|
||||
{@render chatItemContent()}
|
||||
</a>
|
||||
{:else}
|
||||
<LinkPreview.Root
|
||||
openDelay={300}
|
||||
closeDelay={0}
|
||||
disabled={confirmEdit || dragged || !($settings?.chatHoverPreview ?? true)}
|
||||
bind:open={openPreview}
|
||||
onOpenChange={(open) => {
|
||||
if (open) openProfilePreview = false;
|
||||
}}
|
||||
>
|
||||
<LinkPreview.Trigger
|
||||
class="flex min-w-0 flex-1 items-center"
|
||||
href="/c/{id}"
|
||||
aria-current={id === $chatId ? 'page' : undefined}
|
||||
onclick={selectChatHandler}
|
||||
ondblclick={renameChatFromDoubleClick}
|
||||
draggable="false"
|
||||
>
|
||||
{@render chatItemContent()}
|
||||
</LinkPreview.Trigger>
|
||||
|
||||
<ChatHoverPreview
|
||||
chatId={id}
|
||||
title={chatTitle || title}
|
||||
{openPreview}
|
||||
side="right"
|
||||
align="center"
|
||||
/>
|
||||
</LinkPreview.Root>
|
||||
<ChatHoverPreview
|
||||
chatId={id}
|
||||
title={chatTitle || title}
|
||||
{openPreview}
|
||||
side="right"
|
||||
align="center"
|
||||
/>
|
||||
</LinkPreview.Root>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !readonly}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue