dify/web/app/components/explore/installed-app/index.tsx
Jingyi 9b74df21d0
feat(web): refine onboarding UI (#37433)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
Co-authored-by: gigglewang <gigglewang@dify.ai>
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
Co-authored-by: chariri <w@chariri.moe>
Co-authored-by: Evan <2869018789@qq.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-06-15 08:47:15 +00:00

167 lines
6.2 KiB
TypeScript

'use client'
import type { AccessMode } from '@/models/access-control'
import type { AppData } from '@/models/share'
import * as React from 'react'
import { useEffect } from 'react'
import Loading from '@/app/components/base/loading'
import TextGenerationApp from '@/app/components/share/text-generation'
import { useWebAppStore } from '@/context/web-app-context'
import dynamic from '@/next/dynamic'
import { useGetUserCanAccessApp } from '@/service/access-control'
import { useGetInstalledAppAccessModeByAppId, useGetInstalledAppMeta, useGetInstalledAppParams, useGetInstalledApps } from '@/service/use-explore'
import { AppModeEnum } from '@/types/app'
import AppUnavailable from '../../base/app-unavailable'
const ChatWithHistory = dynamic(() => import('@/app/components/base/chat/chat-with-history'), { ssr: false })
const InstalledAppFrame = ({ children }: { children: React.ReactNode }) => (
<div className="h-full bg-background-body pt-2 pl-2">
{children}
</div>
)
const installedAppSurfaceClassName = 'rounded-tr-none rounded-bl-none border-t-4 border-l-4 border-components-chat-input-border'
const InstalledTextGenerationSurface = ({ children }: { children: React.ReactNode }) => (
<div className={`h-full overflow-hidden rounded-2xl shadow-md ${installedAppSurfaceClassName}`}>
{children}
</div>
)
const InstalledApp = ({
id,
}: {
id: string
}) => {
const { data, isPending: isPendingInstalledApps, isFetching: isFetchingInstalledApps } = useGetInstalledApps()
const installedApp = data?.installed_apps?.find(item => item.id === id)
const updateAppInfo = useWebAppStore(s => s.updateAppInfo)
const updateWebAppAccessMode = useWebAppStore(s => s.updateWebAppAccessMode)
const updateAppParams = useWebAppStore(s => s.updateAppParams)
const updateWebAppMeta = useWebAppStore(s => s.updateWebAppMeta)
const updateUserCanAccessApp = useWebAppStore(s => s.updateUserCanAccessApp)
const { isPending: isPendingWebAppAccessMode, data: webAppAccessMode, error: webAppAccessModeError } = useGetInstalledAppAccessModeByAppId(installedApp?.id ?? null)
const { isPending: isPendingAppParams, data: appParams, error: appParamsError } = useGetInstalledAppParams(installedApp?.id ?? null)
const { isPending: isPendingAppMeta, data: appMeta, error: appMetaError } = useGetInstalledAppMeta(installedApp?.id ?? null)
const { data: userCanAccessApp, error: useCanAccessAppError } = useGetUserCanAccessApp({ appId: installedApp?.app.id, isInstalledApp: true })
useEffect(() => {
if (!installedApp) {
updateAppInfo(null)
}
else {
const { id, app } = installedApp
updateAppInfo({
app_id: id,
site: {
title: app.name,
icon_type: app.icon_type,
icon: app.icon,
icon_background: app.icon_background,
icon_url: app.icon_url,
prompt_public: false,
copyright: '',
show_workflow_steps: true,
use_icon_as_answer_icon: app.use_icon_as_answer_icon,
},
plan: 'basic',
custom_config: null,
} as AppData)
}
if (appParams)
updateAppParams(appParams)
if (appMeta)
updateWebAppMeta(appMeta)
if (webAppAccessMode)
updateWebAppAccessMode((webAppAccessMode as { accessMode: AccessMode }).accessMode)
updateUserCanAccessApp(Boolean(userCanAccessApp && (userCanAccessApp as { result: boolean })?.result))
}, [installedApp, appMeta, appParams, updateAppInfo, updateAppParams, updateUserCanAccessApp, updateWebAppMeta, userCanAccessApp, webAppAccessMode, updateWebAppAccessMode])
if (appParamsError) {
return (
<InstalledAppFrame>
<div className="flex h-full items-center justify-center">
<AppUnavailable unknownReason={appParamsError.message} />
</div>
</InstalledAppFrame>
)
}
if (appMetaError) {
return (
<InstalledAppFrame>
<div className="flex h-full items-center justify-center">
<AppUnavailable unknownReason={appMetaError.message} />
</div>
</InstalledAppFrame>
)
}
if (useCanAccessAppError) {
return (
<InstalledAppFrame>
<div className="flex h-full items-center justify-center">
<AppUnavailable unknownReason={useCanAccessAppError.message} />
</div>
</InstalledAppFrame>
)
}
if (webAppAccessModeError) {
return (
<InstalledAppFrame>
<div className="flex h-full items-center justify-center">
<AppUnavailable unknownReason={webAppAccessModeError.message} />
</div>
</InstalledAppFrame>
)
}
if (userCanAccessApp && !userCanAccessApp.result) {
return (
<InstalledAppFrame>
<div className="flex h-full flex-col items-center justify-center gap-y-2">
<AppUnavailable className="size-auto" code={403} unknownReason="no permission." />
</div>
</InstalledAppFrame>
)
}
if (
isPendingInstalledApps
|| (!installedApp && isFetchingInstalledApps)
|| (installedApp && (isPendingAppParams || isPendingAppMeta || isPendingWebAppAccessMode))
) {
return (
<InstalledAppFrame>
<div className="flex h-full items-center justify-center">
<Loading />
</div>
</InstalledAppFrame>
)
}
if (!installedApp) {
return (
<InstalledAppFrame>
<div className="flex h-full items-center justify-center">
<AppUnavailable code={404} isUnknownReason />
</div>
</InstalledAppFrame>
)
}
return (
<InstalledAppFrame>
{installedApp?.app.mode !== AppModeEnum.COMPLETION && installedApp?.app.mode !== AppModeEnum.WORKFLOW && (
<ChatWithHistory installedAppInfo={installedApp} className={`overflow-hidden rounded-2xl shadow-md ${installedAppSurfaceClassName}`} />
)}
{installedApp?.app.mode === AppModeEnum.COMPLETION && (
<InstalledTextGenerationSurface>
<TextGenerationApp isInstalledApp installedAppInfo={installedApp} />
</InstalledTextGenerationSurface>
)}
{installedApp?.app.mode === AppModeEnum.WORKFLOW && (
<InstalledTextGenerationSurface>
<TextGenerationApp isWorkflow isInstalledApp installedAppInfo={installedApp} />
</InstalledTextGenerationSurface>
)}
</InstalledAppFrame>
)
}
export default React.memo(InstalledApp)