Skip to content

Commit 19f2a74

Browse files
authored
fix: check dsl version when create app from explore template (#18872)
1 parent 58a929e commit 19f2a74

File tree

4 files changed

+260
-38
lines changed

4 files changed

+260
-38
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { useTranslation } from 'react-i18next'
2+
import Modal from '@/app/components/base/modal'
3+
import Button from '@/app/components/base/button'
4+
5+
type DSLConfirmModalProps = {
6+
versions?: {
7+
importedVersion: string
8+
systemVersion: string
9+
}
10+
onCancel: () => void
11+
onConfirm: () => void
12+
confirmDisabled?: boolean
13+
}
14+
const DSLConfirmModal = ({
15+
versions = { importedVersion: '', systemVersion: '' },
16+
onCancel,
17+
onConfirm,
18+
confirmDisabled = false,
19+
}: DSLConfirmModalProps) => {
20+
const { t } = useTranslation()
21+
22+
return (
23+
<Modal
24+
isShow
25+
onClose={() => onCancel()}
26+
className='w-[480px]'
27+
>
28+
<div className='flex flex-col items-start gap-2 self-stretch pb-4'>
29+
<div className='title-2xl-semi-bold text-text-primary'>{t('app.newApp.appCreateDSLErrorTitle')}</div>
30+
<div className='system-md-regular flex grow flex-col text-text-secondary'>
31+
<div>{t('app.newApp.appCreateDSLErrorPart1')}</div>
32+
<div>{t('app.newApp.appCreateDSLErrorPart2')}</div>
33+
<br />
34+
<div>{t('app.newApp.appCreateDSLErrorPart3')}<span className='system-md-medium'>{versions.importedVersion}</span></div>
35+
<div>{t('app.newApp.appCreateDSLErrorPart4')}<span className='system-md-medium'>{versions.systemVersion}</span></div>
36+
</div>
37+
</div>
38+
<div className='flex items-start justify-end gap-2 self-stretch pt-6'>
39+
<Button variant='secondary' onClick={() => onCancel()}>{t('app.newApp.Cancel')}</Button>
40+
<Button variant='primary' destructive onClick={onConfirm} disabled={confirmDisabled}>{t('app.newApp.Confirm')}</Button>
41+
</div>
42+
</Modal>
43+
)
44+
}
45+
46+
export default DSLConfirmModal

web/app/components/explore/app-list/index.tsx

+48-37
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
'use client'
22

3-
import React, { useMemo, useState } from 'react'
4-
import { useRouter } from 'next/navigation'
3+
import React, { useCallback, useMemo, useState } from 'react'
54
import { useTranslation } from 'react-i18next'
65
import { useContext } from 'use-context-selector'
76
import useSWR from 'swr'
87
import { useDebounceFn } from 'ahooks'
9-
import Toast from '../../base/toast'
108
import s from './style.module.css'
119
import cn from '@/utils/classnames'
1210
import ExploreContext from '@/context/explore-context'
1311
import type { App } from '@/models/explore'
1412
import Category from '@/app/components/explore/category'
1513
import AppCard from '@/app/components/explore/app-card'
1614
import { fetchAppDetail, fetchAppList } from '@/service/explore'
17-
import { importDSL } from '@/service/apps'
1815
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
1916
import CreateAppModal from '@/app/components/explore/create-app-modal'
2017
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
2118
import Loading from '@/app/components/base/loading'
22-
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
23-
import { useAppContext } from '@/context/app-context'
24-
import { getRedirection } from '@/utils/app-redirection'
2519
import Input from '@/app/components/base/input'
26-
import { DSLImportMode } from '@/models/app'
27-
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
20+
import {
21+
DSLImportMode,
22+
} from '@/models/app'
23+
import { useImportDSL } from '@/hooks/use-import-dsl'
24+
import DSLConfirmModal from '@/app/components/app/create-from-dsl-modal/dsl-confirm-modal'
2825

2926
type AppsProps = {
3027
onSuccess?: () => void
@@ -39,8 +36,6 @@ const Apps = ({
3936
onSuccess,
4037
}: AppsProps) => {
4138
const { t } = useTranslation()
42-
const { isCurrentWorkspaceEditor } = useAppContext()
43-
const { push } = useRouter()
4439
const { hasEditPermission } = useContext(ExploreContext)
4540
const allCategoriesEn = t('explore.apps.allCategories', { lng: 'en' })
4641

@@ -115,44 +110,49 @@ const Apps = ({
115110

116111
const [currApp, setCurrApp] = React.useState<App | null>(null)
117112
const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
118-
const { handleCheckPluginDependencies } = usePluginDependencies()
113+
114+
const {
115+
handleImportDSL,
116+
handleImportDSLConfirm,
117+
versions,
118+
isFetching,
119+
} = useImportDSL()
120+
const [showDSLConfirmModal, setShowDSLConfirmModal] = useState(false)
119121
const onCreate: CreateAppModalProps['onConfirm'] = async ({
120122
name,
121123
icon_type,
122124
icon,
123125
icon_background,
124126
description,
125127
}) => {
126-
const { export_data, mode } = await fetchAppDetail(
128+
const { export_data } = await fetchAppDetail(
127129
currApp?.app.id as string,
128130
)
129-
try {
130-
const app = await importDSL({
131-
mode: DSLImportMode.YAML_CONTENT,
132-
yaml_content: export_data,
133-
name,
134-
icon_type,
135-
icon,
136-
icon_background,
137-
description,
138-
})
139-
setIsShowCreateModal(false)
140-
Toast.notify({
141-
type: 'success',
142-
message: t('app.newApp.appCreated'),
143-
})
144-
if (onSuccess)
145-
onSuccess()
146-
if (app.app_id)
147-
await handleCheckPluginDependencies(app.app_id)
148-
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
149-
getRedirection(isCurrentWorkspaceEditor, { id: app.app_id!, mode }, push)
150-
}
151-
catch {
152-
Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
131+
const payload = {
132+
mode: DSLImportMode.YAML_CONTENT,
133+
yaml_content: export_data,
134+
name,
135+
icon_type,
136+
icon,
137+
icon_background,
138+
description,
153139
}
140+
await handleImportDSL(payload, {
141+
onSuccess: () => {
142+
setIsShowCreateModal(false)
143+
},
144+
onPending: () => {
145+
setShowDSLConfirmModal(true)
146+
},
147+
})
154148
}
155149

150+
const onConfirmDSL = useCallback(async () => {
151+
await handleImportDSLConfirm({
152+
onSuccess,
153+
})
154+
}, [handleImportDSLConfirm, onSuccess])
155+
156156
if (!categories || categories.length === 0) {
157157
return (
158158
<div className="flex h-full items-center">
@@ -225,9 +225,20 @@ const Apps = ({
225225
appDescription={currApp?.app.description || ''}
226226
show={isShowCreateModal}
227227
onConfirm={onCreate}
228+
confirmDisabled={isFetching}
228229
onHide={() => setIsShowCreateModal(false)}
229230
/>
230231
)}
232+
{
233+
showDSLConfirmModal && (
234+
<DSLConfirmModal
235+
versions={versions}
236+
onCancel={() => setShowDSLConfirmModal(false)}
237+
onConfirm={onConfirmDSL}
238+
confirmDisabled={isFetching}
239+
/>
240+
)
241+
}
231242
</div>
232243
)
233244
}

web/app/components/explore/create-app-modal/index.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type CreateAppModalProps = {
3535
description: string
3636
use_icon_as_answer_icon?: boolean
3737
}) => Promise<void>
38+
confirmDisabled?: boolean
3839
onHide: () => void
3940
}
4041

@@ -50,6 +51,7 @@ const CreateAppModal = ({
5051
appMode,
5152
appUseIconAsAnswerIcon,
5253
onConfirm,
54+
confirmDisabled,
5355
onHide,
5456
}: CreateAppModalProps) => {
5557
const { t } = useTranslation()
@@ -160,7 +162,7 @@ const CreateAppModal = ({
160162
</div>
161163
<div className='flex flex-row-reverse'>
162164
<Button
163-
disabled={(!isEditModal && isAppsFull) || !name.trim()}
165+
disabled={(!isEditModal && isAppsFull) || !name.trim() || confirmDisabled}
164166
className='ml-2 w-24 gap-1'
165167
variant='primary'
166168
onClick={handleSubmit}

web/hooks/use-import-dsl.ts

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import {
2+
useCallback,
3+
useRef,
4+
useState,
5+
} from 'react'
6+
import { useTranslation } from 'react-i18next'
7+
import { useRouter } from 'next/navigation'
8+
import type {
9+
DSLImportMode,
10+
DSLImportResponse,
11+
} from '@/models/app'
12+
import { DSLImportStatus } from '@/models/app'
13+
import {
14+
importDSL,
15+
importDSLConfirm,
16+
} from '@/service/apps'
17+
import type { AppIconType } from '@/types/app'
18+
import { useToastContext } from '@/app/components/base/toast'
19+
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
20+
import { getRedirection } from '@/utils/app-redirection'
21+
import { useSelector } from '@/context/app-context'
22+
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
23+
24+
type DSLPayload = {
25+
mode: DSLImportMode
26+
yaml_content?: string
27+
yaml_url?: string
28+
name?: string
29+
icon_type?: AppIconType
30+
icon?: string
31+
icon_background?: string
32+
description?: string
33+
}
34+
type ResponseCallback = {
35+
onSuccess?: () => void
36+
onPending?: (payload: DSLImportResponse) => void
37+
onFailed?: () => void
38+
}
39+
export const useImportDSL = () => {
40+
const { t } = useTranslation()
41+
const { notify } = useToastContext()
42+
const [isFetching, setIsFetching] = useState(false)
43+
const { handleCheckPluginDependencies } = usePluginDependencies()
44+
const isCurrentWorkspaceEditor = useSelector(s => s.isCurrentWorkspaceEditor)
45+
const { push } = useRouter()
46+
const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>()
47+
const importIdRef = useRef<string>('')
48+
49+
const handleImportDSL = useCallback(async (
50+
payload: DSLPayload,
51+
{
52+
onSuccess,
53+
onPending,
54+
onFailed,
55+
}: ResponseCallback,
56+
) => {
57+
if (isFetching)
58+
return
59+
setIsFetching(true)
60+
61+
try {
62+
const response = await importDSL(payload)
63+
64+
if (!response)
65+
return
66+
67+
const {
68+
id,
69+
status,
70+
app_id,
71+
app_mode,
72+
imported_dsl_version,
73+
current_dsl_version,
74+
} = response
75+
76+
if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) {
77+
if (!app_id)
78+
return
79+
80+
notify({
81+
type: status === DSLImportStatus.COMPLETED ? 'success' : 'warning',
82+
message: t(status === DSLImportStatus.COMPLETED ? 'app.newApp.appCreated' : 'app.newApp.caution'),
83+
children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('app.newApp.appCreateDSLWarning'),
84+
})
85+
onSuccess?.()
86+
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
87+
await handleCheckPluginDependencies(app_id)
88+
getRedirection(isCurrentWorkspaceEditor, { id: app_id, mode: app_mode }, push)
89+
}
90+
else if (status === DSLImportStatus.PENDING) {
91+
setVersions({
92+
importedVersion: imported_dsl_version ?? '',
93+
systemVersion: current_dsl_version ?? '',
94+
})
95+
importIdRef.current = id
96+
onPending?.(response)
97+
}
98+
else {
99+
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
100+
onFailed?.()
101+
}
102+
}
103+
catch {
104+
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
105+
onFailed?.()
106+
}
107+
finally {
108+
setIsFetching(false)
109+
}
110+
}, [t, notify, handleCheckPluginDependencies, isCurrentWorkspaceEditor, push, isFetching])
111+
112+
const handleImportDSLConfirm = useCallback(async (
113+
{
114+
onSuccess,
115+
onFailed,
116+
}: Pick<ResponseCallback, 'onSuccess' | 'onFailed'>,
117+
) => {
118+
if (isFetching)
119+
return
120+
setIsFetching(true)
121+
if (!importIdRef.current)
122+
return
123+
124+
try {
125+
const response = await importDSLConfirm({
126+
import_id: importIdRef.current,
127+
})
128+
129+
const { status, app_id, app_mode } = response
130+
if (!app_id)
131+
return
132+
133+
if (status === DSLImportStatus.COMPLETED) {
134+
onSuccess?.()
135+
notify({
136+
type: 'success',
137+
message: t('app.newApp.appCreated'),
138+
})
139+
await handleCheckPluginDependencies(app_id)
140+
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
141+
getRedirection(isCurrentWorkspaceEditor, { id: app_id!, mode: app_mode }, push)
142+
}
143+
else if (status === DSLImportStatus.FAILED) {
144+
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
145+
onFailed?.()
146+
}
147+
}
148+
catch {
149+
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
150+
onFailed?.()
151+
}
152+
finally {
153+
setIsFetching(false)
154+
}
155+
}, [t, notify, handleCheckPluginDependencies, isCurrentWorkspaceEditor, push, isFetching])
156+
157+
return {
158+
handleImportDSL,
159+
handleImportDSLConfirm,
160+
versions,
161+
isFetching,
162+
}
163+
}

0 commit comments

Comments
 (0)