fix: show App is UnAvailable when no app info found in explore page

This commit is contained in:
NFish 2025-07-14 11:29:37 +08:00
parent 8ead8961ce
commit 3e18dd41e7
5 changed files with 67 additions and 21 deletions

View File

@ -22,6 +22,7 @@ const Explore: FC<IExploreProps> = ({
const { userProfile, isCurrentWorkspaceDatasetOperator } = useAppContext()
const [hasEditPermission, setHasEditPermission] = useState(false)
const [installedApps, setInstalledApps] = useState<InstalledApp[]>([])
const [isFetchingInstalledApps, setIsFetchingInstalledApps] = useState(false)
const { t } = useTranslation()
useDocumentTitle(t('common.menus.explore'))
@ -51,6 +52,8 @@ const Explore: FC<IExploreProps> = ({
hasEditPermission,
installedApps,
setInstalledApps,
isFetchingInstalledApps,
setIsFetchingInstalledApps,
}
}
>

View File

@ -20,7 +20,7 @@ export type IInstalledAppProps = {
const InstalledApp: FC<IInstalledAppProps> = ({
id,
}) => {
const { installedApps } = useContext(ExploreContext)
const { installedApps, isFetchingInstalledApps } = useContext(ExploreContext)
const updateAppInfo = useWebAppStore(s => s.updateAppInfo)
const installedApp = installedApps.find(item => item.id === id)
const updateWebAppAccessMode = useWebAppStore(s => s.updateWebAppAccessMode)
@ -90,21 +90,25 @@ const InstalledApp: FC<IInstalledAppProps> = ({
<AppUnavailable className='h-auto w-auto' code={403} unknownReason='no permission.' />
</div>
}
if (isFetchingAppParams || isFetchingAppMeta || isFetchingWebAppAccessMode || !installedApp) {
if (isFetchingAppParams || isFetchingAppMeta || isFetchingWebAppAccessMode || isFetchingInstalledApps) {
return <div className='flex h-full items-center justify-center'>
<Loading />
</div>
}
if (!installedApp) {
return <div className='flex h-full items-center justify-center'>
<AppUnavailable code={404} isUnknownReason />
</div>
}
return (
<div className='h-full bg-background-default py-2 pl-0 pr-2 sm:p-2'>
{installedApp.app.mode !== 'completion' && installedApp.app.mode !== 'workflow' && (
{installedApp?.app.mode !== 'completion' && installedApp?.app.mode !== 'workflow' && (
<ChatWithHistory installedAppInfo={installedApp} className='overflow-hidden rounded-2xl shadow-md' />
)}
{installedApp.app.mode === 'completion' && (
{installedApp?.app.mode === 'completion' && (
<TextGenerationApp isInstalledApp installedAppInfo={installedApp} />
)}
{installedApp.app.mode === 'workflow' && (
{installedApp?.app.mode === 'workflow' && (
<TextGenerationApp isWorkflow isInstalledApp installedAppInfo={installedApp} />
)}
</div>

View File

@ -8,11 +8,11 @@ import Link from 'next/link'
import Toast from '../../base/toast'
import Item from './app-nav-item'
import cn from '@/utils/classnames'
import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp, updatePinStatus } from '@/service/explore'
import ExploreContext from '@/context/explore-context'
import Confirm from '@/app/components/base/confirm'
import Divider from '@/app/components/base/divider'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useGetInstalledApps, useUninstallApp, useUpdateAppPinStatus } from '@/service/use-explore'
const SelectedDiscoveryIcon = () => (
<svg width="16" height="16" viewBox="0 0 16 16" fill="current" xmlns="http://www.w3.org/2000/svg">
@ -50,16 +50,14 @@ const SideBar: FC<IExploreSideBarProps> = ({
const lastSegment = segments.slice(-1)[0]
const isDiscoverySelected = lastSegment === 'apps'
const isChatSelected = lastSegment === 'chat'
const { installedApps, setInstalledApps } = useContext(ExploreContext)
const { installedApps, setInstalledApps, setIsFetchingInstalledApps } = useContext(ExploreContext)
const { isFetching: isFetchingInstalledApps, data: ret, refetch: fetchInstalledAppList } = useGetInstalledApps()
const { mutateAsync: uninstallApp } = useUninstallApp()
const { mutateAsync: updatePinStatus } = useUpdateAppPinStatus()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const fetchInstalledAppList = async () => {
const { installed_apps }: any = await doFetchInstalledAppList()
setInstalledApps(installed_apps)
}
const [showConfirm, setShowConfirm] = useState(false)
const [currId, setCurrId] = useState('')
const handleDelete = async () => {
@ -70,25 +68,31 @@ const SideBar: FC<IExploreSideBarProps> = ({
type: 'success',
message: t('common.api.remove'),
})
fetchInstalledAppList()
}
const handleUpdatePinStatus = async (id: string, isPinned: boolean) => {
await updatePinStatus(id, isPinned)
await updatePinStatus({ appId: id, isPinned })
Toast.notify({
type: 'success',
message: t('common.api.success'),
})
fetchInstalledAppList()
}
useEffect(() => {
fetchInstalledAppList()
}, [])
const installed_apps = (ret as any)?.installed_apps
if (installed_apps && installed_apps.length > 0)
setInstalledApps(installed_apps)
else
setInstalledApps([])
}, [ret, setInstalledApps])
useEffect(() => {
setIsFetchingInstalledApps(isFetchingInstalledApps)
}, [isFetchingInstalledApps, setIsFetchingInstalledApps])
useEffect(() => {
fetchInstalledAppList()
}, [controlUpdateInstalledApps])
}, [controlUpdateInstalledApps, fetchInstalledAppList])
const pinnedAppsCount = installedApps.filter(({ is_pinned }) => is_pinned).length
return (

View File

@ -8,6 +8,8 @@ type IExplore = {
hasEditPermission: boolean
installedApps: InstalledApp[]
setInstalledApps: (installedApps: InstalledApp[]) => void
isFetchingInstalledApps: boolean
setIsFetchingInstalledApps: (isFetchingInstalledApps: boolean) => void
}
const ExploreContext = createContext<IExplore>({
@ -16,6 +18,8 @@ const ExploreContext = createContext<IExplore>({
hasEditPermission: false,
installedApps: [],
setInstalledApps: noop,
isFetchingInstalledApps: false,
setIsFetchingInstalledApps: noop,
})
export default ExploreContext

View File

@ -1,11 +1,42 @@
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AccessMode } from '@/models/access-control'
import { useQuery } from '@tanstack/react-query'
import { getAppAccessModeByAppId } from './explore'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { fetchInstalledAppList, getAppAccessModeByAppId, uninstallApp, updatePinStatus } from './explore'
import { fetchAppMeta, fetchAppParams } from './share'
const NAME_SPACE = 'explore'
export const useGetInstalledApps = () => {
return useQuery({
queryKey: [NAME_SPACE, 'installedApps'],
queryFn: () => {
return fetchInstalledAppList()
},
})
}
export const useUninstallApp = () => {
const client = useQueryClient()
return useMutation({
mutationKey: [NAME_SPACE, 'uninstallApp'],
mutationFn: (appId: string) => uninstallApp(appId),
onSuccess: () => {
client.invalidateQueries({ queryKey: [NAME_SPACE, 'installedApps'] })
},
})
}
export const useUpdateAppPinStatus = () => {
const client = useQueryClient()
return useMutation({
mutationKey: [NAME_SPACE, 'updateAppPinStatus'],
mutationFn: ({ appId, isPinned }: { appId: string; isPinned: boolean }) => updatePinStatus(appId, isPinned),
onSuccess: () => {
client.invalidateQueries({ queryKey: [NAME_SPACE, 'installedApps'] })
},
})
}
export const useGetInstalledAppAccessModeByAppId = (appId: string | null) => {
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
return useQuery({