mirror of
https://github.com/langgenius/dify.git
synced 2026-08-28 20:33:21 +08:00
fix: add missing app list page online users
This commit is contained in:
parent
8e38a19de7
commit
0220006c50
@ -5,6 +5,7 @@ import type { HtmlContentProps } from '@/app/components/base/popover'
|
||||
import type { Tag } from '@/app/components/base/tag-management/constant'
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import type { EnvironmentVariable } from '@/app/components/workflow/types'
|
||||
import type { WorkflowOnlineUser } from '@/models/app'
|
||||
import type { App } from '@/types/app'
|
||||
import { RiBuildingLine, RiGlobalLine, RiLockLine, RiMoreFill, RiVerifiedBadgeLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
@ -27,6 +28,7 @@ import {
|
||||
AlertDialogTitle,
|
||||
} from '@/app/components/base/ui/alert-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { UserAvatarList } from '@/app/components/base/user-avatar-list'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
@ -65,10 +67,11 @@ const AccessControl = dynamic(() => import('@/app/components/app/app-access-cont
|
||||
|
||||
type AppCardProps = {
|
||||
app: App
|
||||
onlineUsers?: WorkflowOnlineUser[]
|
||||
onRefresh?: () => void
|
||||
}
|
||||
|
||||
const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||
const AppCard = ({ app, onlineUsers = [], onRefresh }: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const deleteAppNameInputId = useId()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
@ -360,6 +363,20 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||
return `${t('segment.editedAt', { ns: 'datasetDocuments' })} ${timeText}`
|
||||
}, [app.updated_at, app.created_at, t])
|
||||
|
||||
const onlinePresenceUsers = useMemo(() => {
|
||||
return onlineUsers
|
||||
.map((user, index) => {
|
||||
const id = user.user_id || user.sid || `${app.id}-online-${index}`
|
||||
const name = user.username || user.user_id || user.sid || `${index + 1}`
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
avatar_url: user.avatar || null,
|
||||
}
|
||||
})
|
||||
.filter(user => Boolean(user.id))
|
||||
}, [app.id, onlineUsers])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@ -389,6 +406,11 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||
<div>·</div>
|
||||
<div className="truncate" title={EditTimeText}>{EditTimeText}</div>
|
||||
</div>
|
||||
{onlinePresenceUsers.length > 0 && (
|
||||
<div className="mt-1">
|
||||
<UserAvatarList users={onlinePresenceUsers} size="xxs" maxVisible={4} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex h-5 w-5 shrink-0 items-center justify-center">
|
||||
{app.access_mode === AccessMode.PUBLIC && (
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import type { WorkflowOnlineUser } from '@/models/app'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import { parseAsStringLiteral, useQueryState } from 'nuqs'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import Input from '@/app/components/base/input'
|
||||
@ -15,6 +16,7 @@ import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { CheckModal } from '@/hooks/use-pay'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { fetchWorkflowOnlineUsers } from '@/service/apps'
|
||||
import { useInfiniteAppList } from '@/service/use-apps'
|
||||
import { AppModeEnum, AppModes } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -68,6 +70,7 @@ const List: FC<Props> = ({
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [showCreateFromDSLModal, setShowCreateFromDSLModal] = useState(false)
|
||||
const [droppedDSLFile, setDroppedDSLFile] = useState<File | undefined>()
|
||||
const [workflowOnlineUsersMap, setWorkflowOnlineUsersMap] = useState<Record<string, WorkflowOnlineUser[]>>({})
|
||||
const setKeywords = useCallback((keywords: string) => {
|
||||
setQuery(prev => ({ ...prev, keywords }))
|
||||
}, [setQuery])
|
||||
@ -183,6 +186,44 @@ const List: FC<Props> = ({
|
||||
}, [isCreatedByMe, setQuery])
|
||||
|
||||
const pages = data?.pages ?? []
|
||||
const workflowIds = useMemo(() => {
|
||||
const ids = new Set<string>()
|
||||
pages.forEach((page) => {
|
||||
page.data?.forEach((app) => {
|
||||
const workflowId = app.workflow?.id
|
||||
if (workflowId)
|
||||
ids.add(workflowId)
|
||||
})
|
||||
})
|
||||
return Array.from(ids)
|
||||
}, [pages])
|
||||
|
||||
const refreshWorkflowOnlineUsers = useCallback(async () => {
|
||||
if (!workflowIds.length)
|
||||
return
|
||||
|
||||
try {
|
||||
const onlineUsersMap = await fetchWorkflowOnlineUsers({ workflowIds })
|
||||
setWorkflowOnlineUsersMap(onlineUsersMap)
|
||||
}
|
||||
catch {
|
||||
setWorkflowOnlineUsersMap({})
|
||||
}
|
||||
}, [workflowIds])
|
||||
|
||||
useEffect(() => {
|
||||
void refreshWorkflowOnlineUsers()
|
||||
}, [refreshWorkflowOnlineUsers])
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setInterval(() => {
|
||||
void refetch()
|
||||
void refreshWorkflowOnlineUsers()
|
||||
}, 10000)
|
||||
|
||||
return () => window.clearInterval(timer)
|
||||
}, [refetch, refreshWorkflowOnlineUsers])
|
||||
|
||||
const hasAnyApp = (pages[0]?.total ?? 0) > 0
|
||||
// Show skeleton during initial load or when refetching with no previous data
|
||||
const showSkeleton = isLoading || (isFetching && pages.length === 0)
|
||||
@ -242,7 +283,12 @@ const List: FC<Props> = ({
|
||||
|
||||
if (hasAnyApp) {
|
||||
return pages.flatMap(({ data: apps }) => apps).map(app => (
|
||||
<AppCard key={app.id} app={app} onRefresh={refetch} />
|
||||
<AppCard
|
||||
key={app.id}
|
||||
app={app}
|
||||
onlineUsers={app.workflow?.id ? (workflowOnlineUsersMap[app.workflow.id] ?? []) : []}
|
||||
onRefresh={refetch}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user