diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index a7f53e4e9..7006091e1 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -10,6 +10,9 @@ from open_webui.models.users import ( UserSettings, UserUpdateForm, ) + + +from open_webui.socket.main import get_active_status_by_user_id from open_webui.constants import ERROR_MESSAGES from open_webui.env import SRC_LOG_LEVELS from fastapi import APIRouter, Depends, HTTPException, Request, status @@ -196,6 +199,7 @@ async def update_user_info_by_session_user( class UserResponse(BaseModel): name: str profile_image_url: str + active: Optional[bool] = None @router.get("/{user_id}", response_model=UserResponse) @@ -216,7 +220,13 @@ async def get_user_by_id(user_id: str, user=Depends(get_verified_user)): user = Users.get_user_by_id(user_id) if user: - return UserResponse(name=user.name, profile_image_url=user.profile_image_url) + return UserResponse( + **{ + "name": user.name, + "profile_image_url": user.profile_image_url, + "active": get_active_status_by_user_id(user_id), + } + ) else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index bed6ada27..c0bb3c662 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -159,7 +159,7 @@ async def connect(sid, environ, auth): USER_POOL[user.id] = [sid] # print(f"user {user.name}({user.id}) connected with session ID {sid}") - await sio.emit("user-count", {"count": len(USER_POOL.items())}) + await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())}) await sio.emit("usage", {"models": get_models_in_use()}) @@ -192,7 +192,7 @@ async def user_join(sid, data): # print(f"user {user.name}({user.id}) connected with session ID {sid}") - await sio.emit("user-count", {"count": len(USER_POOL.items())}) + await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())}) return {"id": user.id, "name": user.name} @@ -244,9 +244,9 @@ async def channel_events(sid, data): ) -@sio.on("user-count") -async def user_count(sid): - await sio.emit("user-count", {"count": len(USER_POOL.items())}) +@sio.on("user-list") +async def user_list(sid): + await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())}) @sio.event @@ -261,7 +261,7 @@ async def disconnect(sid): if len(USER_POOL[user_id]) == 0: del USER_POOL[user_id] - await sio.emit("user-count", {"count": len(USER_POOL)}) + await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())}) else: pass # print(f"Unknown session ID {sid} disconnected") @@ -330,3 +330,9 @@ def get_user_ids_from_room(room): ) ) return active_user_ids + + +def get_active_status_by_user_id(user_id): + if user_id in USER_POOL: + return True + return False diff --git a/src/lib/components/channel/Messages/Message.svelte b/src/lib/components/channel/Messages/Message.svelte index 0cf093ad6..89cbfd6d1 100644 --- a/src/lib/components/channel/Messages/Message.svelte +++ b/src/lib/components/channel/Messages/Message.svelte @@ -25,6 +25,7 @@ import Textarea from '$lib/components/common/Textarea.svelte'; import Image from '$lib/components/common/Image.svelte'; import FileItem from '$lib/components/common/FileItem.svelte'; + import ProfilePreview from './Message/ProfilePreview.svelte'; export let message; export let showUserProfile = true; @@ -101,11 +102,13 @@ class={`flex-shrink-0 ${($settings?.chatDirection ?? 'LTR') === 'LTR' ? 'mr-3' : 'ml-3'} w-9`} > {#if showUserProfile} - + + + {:else} diff --git a/src/lib/components/channel/Messages/Message/ProfilePreview.svelte b/src/lib/components/channel/Messages/Message/ProfilePreview.svelte new file mode 100644 index 000000000..50398976b --- /dev/null +++ b/src/lib/components/channel/Messages/Message/ProfilePreview.svelte @@ -0,0 +1,85 @@ + + + { + dispatch('change', state); + }} + typeahead={false} +> + + + + + + + {#if user} + + + + + + + + {user.name} + + + + {#if $activeUserIds.includes(user.id)} + + + + + + + + + Active + + {:else} + + + + + + + + Away + + {/if} + + + + {/if} + + + diff --git a/src/lib/components/common/Dropdown.svelte b/src/lib/components/common/Dropdown.svelte index b01bceace..6d07c844e 100644 --- a/src/lib/components/common/Dropdown.svelte +++ b/src/lib/components/common/Dropdown.svelte @@ -5,6 +5,8 @@ import { flyAndScale } from '$lib/utils/transitions'; export let show = false; + export let side = 'bottom'; + export let align = 'start'; const dispatch = createEventDispatcher(); @@ -24,8 +26,8 @@ diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index bddc21150..3775bbf9d 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -5,7 +5,7 @@ import { flyAndScale } from '$lib/utils/transitions'; import { goto } from '$app/navigation'; import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte'; - import { showSettings, activeUserCount, USAGE_POOL, mobile, showSidebar } from '$lib/stores'; + import { showSettings, activeUserIds, USAGE_POOL, mobile, showSidebar } from '$lib/stores'; import { fade, slide } from 'svelte/transition'; import Tooltip from '$lib/components/common/Tooltip.svelte'; import { userSignOut } from '$lib/apis/auths'; @@ -184,7 +184,7 @@ {$i18n.t('Sign Out')} - {#if $activeUserCount} + {#if $activeUserIds?.length > 0} - {$activeUserCount} + {$activeUserIds?.length} diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts index 6f63e773a..65872bcd6 100644 --- a/src/lib/stores/index.ts +++ b/src/lib/stores/index.ts @@ -15,7 +15,7 @@ export const MODEL_DOWNLOAD_POOL = writable({}); export const mobile = writable(false); export const socket: Writable = writable(null); -export const activeUserCount: Writable = writable(null); +export const activeUserIds: Writable = writable(null); export const USAGE_POOL: Writable = writable(null); export const theme = writable('system'); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index d4fb46b9e..fc4bd1c8f 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -14,7 +14,7 @@ WEBUI_NAME, mobile, socket, - activeUserCount, + activeUserIds, USAGE_POOL, chatId, chats, @@ -81,9 +81,9 @@ } }); - _socket.on('user-count', (data) => { - console.log('user-count', data); - activeUserCount.set(data.count); + _socket.on('user-list', (data) => { + console.log('user-list', data); + activeUserIds.set(data.user_ids); }); _socket.on('usage', (data) => {