feat: add humanInputEmailDeliveryEnabled to provider context and update related components for email delivery handling

This commit is contained in:
twwu 2026-01-15 16:13:01 +08:00
parent 88c2483192
commit 61c7fdc614
7 changed files with 46 additions and 23 deletions

View File

@ -35,6 +35,7 @@ export const baseProviderContextValue: ProviderContextState = {
refreshLicenseLimit: noop,
isAllowTransferWorkspace: false,
isAllowPublishAsCustomKnowledgePipelineTemplate: false,
humanInputEmailDeliveryEnabled: false,
}
export const createMockProviderContextValue = (overrides: Partial<ProviderContextState> = {}): ProviderContextState => {

View File

@ -53,6 +53,7 @@ const defaultProviderContext = {
refreshLicenseLimit: noop,
isAllowTransferWorkspace: false,
isAllowPublishAsCustomKnowledgePipelineTemplate: false,
humanInputEmailDeliveryEnabled: false,
}
const defaultModalContext: ModalContextState = {

View File

@ -118,6 +118,7 @@ export type CurrentPlanInfoBackend = {
knowledge_pipeline: {
publish_enabled: boolean
}
human_input_email_delivery_enabled: boolean
}
export type SubscriptionItem = {

View File

@ -35,7 +35,7 @@ import {
import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { useModalContextSelector } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { useProviderContextSelector } from '@/context/provider-context'
import { useDatasetApiAccessUrl } from '@/hooks/use-api-access-url'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
@ -65,7 +65,7 @@ const Popup = () => {
const { mutateAsync: publishWorkflow } = usePublishWorkflow()
const { notify } = useToastContext()
const workflowStore = useWorkflowStore()
const { isAllowPublishAsCustomKnowledgePipelineTemplate } = useProviderContext()
const isAllowPublishAsCustomKnowledgePipelineTemplate = useProviderContextSelector(s => s.isAllowPublishAsCustomKnowledgePipelineTemplate)
const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal)
const apiReferenceUrl = useDatasetApiAccessUrl()
@ -149,7 +149,7 @@ const Popup = () => {
if (confirmVisible)
hideConfirm()
}
}, [handleCheckBeforePublish, publishWorkflow, pipelineId, notify, t, workflowStore, mutateDatasetRes, invalidPublishedPipelineInfo, showConfirm, publishedAt, confirmVisible, hidePublishing, showPublishing, hideConfirm, publishing])
}, [publishing, handleCheckBeforePublish, publishedAt, confirmVisible, showPublishing, publishWorkflow, pipelineId, datasetId, showConfirm, notify, t, workflowStore, mutateDatasetRes, invalidPublishedPipelineInfo, invalidDatasetList, hidePublishing, hideConfirm])
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => {
e.preventDefault()
@ -204,15 +204,7 @@ const Popup = () => {
hidePublishingAsCustomizedPipeline()
hidePublishAsKnowledgePipelineModal()
}
}, [
pipelineId,
publishAsCustomizedPipeline,
showPublishingAsCustomizedPipeline,
hidePublishingAsCustomizedPipeline,
hidePublishAsKnowledgePipelineModal,
notify,
t,
])
}, [showPublishingAsCustomizedPipeline, publishAsCustomizedPipeline, pipelineId, notify, t, invalidCustomizedTemplateList, hidePublishingAsCustomizedPipeline, hidePublishAsKnowledgePipelineModal])
const handleClickPublishAsKnowledgePipeline = useCallback(() => {
if (!isAllowPublishAsCustomKnowledgePipelineTemplate)

View File

@ -98,7 +98,9 @@ const DeliveryMethodItem: FC<DeliveryMethodItemProps> = ({
</div>
)}
<div className="system-xs-medium capitalize text-text-secondary">{method.type}</div>
{method.type === DeliveryMethodType.Email && (method.config as EmailConfig)?.debug_mode && <Badge size="s" className="!px-1 !py-0.5">DEBUG</Badge>}
{method.type === DeliveryMethodType.Email
&& (method.config as EmailConfig)?.debug_mode
&& <Badge size="s" className="!px-1 !py-0.5">DEBUG</Badge>}
</div>
<div className="flex items-center gap-1">
{!readonly && (

View File

@ -8,8 +8,7 @@ import {
RiMailSendFill,
RiRobot2Fill,
} from '@remixicon/react'
import * as React from 'react'
import { useCallback, useRef, useState } from 'react'
import { memo, useCallback, useMemo, useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { v4 as uuid4 } from 'uuid'
import ActionButton from '@/app/components/base/action-button'
@ -21,23 +20,26 @@ import {
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { IS_CE_EDITION } from '@/config'
import { useProviderContextSelector } from '@/context/provider-context'
import { cn } from '@/utils/classnames'
import { DeliveryMethodType } from '../../types'
const i18nPrefix = 'nodes.humanInput'
type Props = {
type MethodSelectorProps = {
data: DeliveryMethod[]
onAdd: (method: DeliveryMethod) => void
}
const MethodSelector: FC<Props> = ({
const MethodSelector: FC<MethodSelectorProps> = ({
data,
onAdd,
}) => {
const { t } = useTranslation()
const [open, doSetOpen] = useState(false)
const humanInputEmailDeliveryEnabled = useProviderContextSelector(s => s.humanInputEmailDeliveryEnabled)
const openRef = useRef(open)
const setOpen = useCallback((v: boolean) => {
doSetOpen(v)
openRef.current = v
@ -47,6 +49,13 @@ const MethodSelector: FC<Props> = ({
setOpen(!openRef.current)
}, [setOpen])
const emailDeliveryInfo = useMemo(() => {
return {
noPermission: !humanInputEmailDeliveryEnabled,
added: data.some(method => method.type === DeliveryMethodType.Email),
}
}, [data, humanInputEmailDeliveryEnabled])
return (
<PortalToFollowElem
open={open}
@ -91,9 +100,12 @@ const MethodSelector: FC<Props> = ({
)}
</div>
<div
className={cn('relative flex cursor-pointer items-center gap-1 rounded-lg p-1 pl-3 hover:bg-state-base-hover', data.some(method => method.type === DeliveryMethodType.Email) && 'cursor-not-allowed bg-transparent hover:bg-transparent')}
className={cn(
'relative flex cursor-pointer items-center gap-1 rounded-lg p-1 pl-3 hover:bg-state-base-hover',
(emailDeliveryInfo.noPermission || emailDeliveryInfo.added) && 'cursor-not-allowed bg-transparent hover:bg-transparent',
)}
onClick={() => {
if (data.some(method => method.type === DeliveryMethodType.Email))
if (emailDeliveryInfo.noPermission || emailDeliveryInfo.added)
return
onAdd({
id: uuid4(),
@ -102,16 +114,24 @@ const MethodSelector: FC<Props> = ({
})
}}
>
<div className={cn('rounded-[4px] border border-divider-regular bg-components-icon-bg-blue-solid p-1', data.some(method => method.type === DeliveryMethodType.Email) && 'opacity-50')}>
<div
className={cn(
'rounded-[4px] border border-divider-regular bg-components-icon-bg-blue-solid p-1',
(emailDeliveryInfo.noPermission || emailDeliveryInfo.added) && 'opacity-50',
)}
>
<RiMailSendFill className="h-4 w-4 text-text-primary-on-surface" />
</div>
<div className={cn('p-1', data.some(method => method.type === DeliveryMethodType.Email) && 'opacity-50')}>
<div className={cn('p-1', (emailDeliveryInfo.noPermission || emailDeliveryInfo.added) && 'opacity-50')}>
<div className="system-sm-medium mb-0.5 truncate text-text-primary">{t(`${i18nPrefix}.deliveryMethod.types.email.title`, { ns: 'workflow' })}</div>
<div className="system-xs-regular truncate text-text-tertiary">{t(`${i18nPrefix}.deliveryMethod.types.email.description`, { ns: 'workflow' })}</div>
</div>
{data.some(method => method.type === DeliveryMethodType.Email) && (
{emailDeliveryInfo.added && (
<div className="system-xs-regular absolute right-[12px] top-[13px] text-text-tertiary">{t(`${i18nPrefix}.deliveryMethod.added`, { ns: 'workflow' })}</div>
)}
{emailDeliveryInfo.noPermission && (
<div className="system-xs-regular absolute right-[12px] top-[13px] text-text-tertiary">Upgrade</div>
)}
</div>
{/* Slack */}
<div
@ -181,4 +201,4 @@ const MethodSelector: FC<Props> = ({
</PortalToFollowElem>
)
}
export default React.memo(MethodSelector)
export default memo(MethodSelector)

View File

@ -64,6 +64,7 @@ export type ProviderContextState = {
refreshLicenseLimit: () => void
isAllowTransferWorkspace: boolean
isAllowPublishAsCustomKnowledgePipelineTemplate: boolean
humanInputEmailDeliveryEnabled: boolean
}
export const baseProviderContextValue: ProviderContextState = {
@ -96,6 +97,7 @@ export const baseProviderContextValue: ProviderContextState = {
refreshLicenseLimit: noop,
isAllowTransferWorkspace: false,
isAllowPublishAsCustomKnowledgePipelineTemplate: false,
humanInputEmailDeliveryEnabled: false,
}
const ProviderContext = createContext<ProviderContextState>(baseProviderContextValue)
@ -137,6 +139,7 @@ export const ProviderContextProvider = ({
const { data: educationAccountInfo, isLoading: isLoadingEducationAccountInfo, isFetching: isFetchingEducationAccountInfo, isFetchedAfterMount: isEducationDataFetchedAfterMount } = useEducationStatus(!enableEducationPlan)
const [isAllowTransferWorkspace, setIsAllowTransferWorkspace] = useState(false)
const [isAllowPublishAsCustomKnowledgePipelineTemplate, setIsAllowPublishAsCustomKnowledgePipelineTemplate] = useState(false)
const [humanInputEmailDeliveryEnabled, setHumanInputEmailDeliveryEnabled] = useState(false)
const refreshModelProviders = () => {
queryClient.invalidateQueries({ queryKey: ['common', 'model-providers'] })
@ -173,6 +176,8 @@ export const ProviderContextProvider = ({
setIsAllowTransferWorkspace(data.is_allow_transfer_workspace)
if (data.knowledge_pipeline?.publish_enabled)
setIsAllowPublishAsCustomKnowledgePipelineTemplate(data.knowledge_pipeline?.publish_enabled)
if (data.human_input_email_delivery_enabled)
setHumanInputEmailDeliveryEnabled(data.human_input_email_delivery_enabled)
}
catch (error) {
console.error('Failed to fetch plan info:', error)
@ -250,6 +255,7 @@ export const ProviderContextProvider = ({
refreshLicenseLimit: fetchPlan,
isAllowTransferWorkspace,
isAllowPublishAsCustomKnowledgePipelineTemplate,
humanInputEmailDeliveryEnabled,
}}
>
{children}