feat: add loading state to Publish button during workflow publishing

Leverage React Query mutation's isPending to disable the Publish button,
header trigger, and keyboard shortcut while a publish is in progress,
preventing duplicate submissions even when the menu is closed and reopened.
This commit is contained in:
yyh
2026-02-04 14:33:51 +08:00
parent dee3e026a6
commit d84aaff825
4 changed files with 21 additions and 12 deletions

View File

@ -127,6 +127,7 @@ export type AppPublisherProps = {
missingStartNode?: boolean
hasTriggerNode?: boolean // Whether workflow currently contains any trigger nodes (used to hide missing-start CTA when triggers exist).
startNodeLimitExceeded?: boolean
publishLoading?: boolean
}
const PUBLISH_SHORTCUT = ['ctrl', '⇧', 'P']
@ -150,6 +151,7 @@ const AppPublisher = ({
missingStartNode = false,
hasTriggerNode = false,
startNodeLimitExceeded = false,
publishLoading = false,
}: AppPublisherProps) => {
const { t } = useTranslation()
@ -295,7 +297,7 @@ const AppPublisher = ({
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => {
e.preventDefault()
if (publishDisabled || published)
if (publishDisabled || published || publishLoading)
return
handlePublish()
}, { exactMatch: true, useCapture: true })
@ -349,7 +351,8 @@ const AppPublisher = ({
<Button
variant="primary"
className="py-2 pl-3 pr-2"
disabled={disabled}
disabled={disabled || publishLoading}
loading={publishLoading}
>
{t('common.publish', { ns: 'workflow' })}
<RiArrowDownSLine className="h-4 w-4 text-components-button-primary-text" />
@ -403,17 +406,20 @@ const AppPublisher = ({
variant="primary"
className="mt-3 w-full"
onClick={() => handlePublish()}
disabled={publishDisabled || published}
disabled={publishDisabled || published || publishLoading}
loading={publishLoading}
>
{
published
? t('common.published', { ns: 'workflow' })
: (
<div className="flex gap-1">
<span>{t('common.publishUpdate', { ns: 'workflow' })}</span>
<ShortcutsName keys={PUBLISH_SHORTCUT} bgColor="white" />
</div>
)
publishLoading
? t('common.publishing', { ns: 'workflow' })
: published
? t('common.published', { ns: 'workflow' })
: (
<div className="flex gap-1">
<span>{t('common.publishUpdate', { ns: 'workflow' })}</span>
<ShortcutsName keys={PUBLISH_SHORTCUT} bgColor="white" />
</div>
)
}
</Button>
{showStartNodeLimitHint && (

View File

@ -136,7 +136,7 @@ const FeaturesTrigger = () => {
}
}, [appID, setAppDetail])
const { mutateAsync: publishWorkflow } = usePublishWorkflow()
const { mutateAsync: publishWorkflow, isPending: isPublishing } = usePublishWorkflow()
// const { validateBeforeRun } = useWorkflowRunValidation()
const needWarningNodes = useChecklist(nodes, edges)
@ -229,6 +229,7 @@ const FeaturesTrigger = () => {
hasTriggerNode,
startNodeLimitExceeded,
publishDisabled: !hasWorkflowNodes || startNodeLimitExceeded,
publishLoading: isPublishing,
}}
/>
</>

View File

@ -223,6 +223,7 @@
"common.publishUpdate": "Publish Update",
"common.published": "Published",
"common.publishedAt": "Published",
"common.publishing": "Publishing...",
"common.redo": "Redo",
"common.restart": "Restart",
"common.restore": "Restore",

View File

@ -221,6 +221,7 @@
"common.publishUpdate": "发布更新",
"common.published": "已发布",
"common.publishedAt": "发布于",
"common.publishing": "发布中...",
"common.redo": "重做",
"common.restart": "重新开始",
"common.restore": "恢复",