mirror of
https://github.com/langgenius/dify.git
synced 2026-06-08 09:27:39 +08:00
221 lines
8.4 KiB
TypeScript
221 lines
8.4 KiB
TypeScript
'use client'
|
|
import type { FC } from 'react'
|
|
import type { AutoUpdateConfig } from './types'
|
|
import type { TriggerParams } from '@/app/components/base/date-and-time-picker/types'
|
|
import { cn } from '@langgenius/dify-ui/cn'
|
|
import { ToggleGroup, ToggleGroupItem } from '@langgenius/dify-ui/toggle-group'
|
|
import { RiTimeLine } from '@remixicon/react'
|
|
import * as React from 'react'
|
|
import { useCallback, useMemo } from 'react'
|
|
import { Trans, useTranslation } from 'react-i18next'
|
|
import TimePicker from '@/app/components/base/date-and-time-picker/time-picker'
|
|
import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs'
|
|
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
|
import { useAppContext } from '@/context/app-context'
|
|
import { useModalContextSelector } from '@/context/modal-context'
|
|
import Label from '../label'
|
|
import PluginsPicker from './plugins-picker'
|
|
import StrategyPicker from './strategy-picker'
|
|
import { AUTO_UPDATE_MODE, AUTO_UPDATE_STRATEGY } from './types'
|
|
import { convertLocalSecondsToUTCDaySeconds, convertUTCDaySecondsToLocalSeconds, dayjsToTimeOfDay, timeOfDayToDayjs } from './utils'
|
|
|
|
const i18nPrefix = 'autoUpdate'
|
|
|
|
type Props = {
|
|
payload: AutoUpdateConfig
|
|
onChange: (payload: AutoUpdateConfig) => void
|
|
}
|
|
|
|
const SettingTimeZone: FC<{
|
|
children?: React.ReactNode
|
|
}> = ({
|
|
children,
|
|
}) => {
|
|
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="cursor-pointer border-none bg-transparent p-0 text-left body-xs-regular text-text-accent focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
|
|
onClick={() => setShowAccountSettingModal({ payload: ACCOUNT_SETTING_TAB.LANGUAGE })}
|
|
>
|
|
{children}
|
|
</button>
|
|
)
|
|
}
|
|
const AutoUpdateSetting: FC<Props> = ({
|
|
payload,
|
|
onChange,
|
|
}) => {
|
|
const { t } = useTranslation()
|
|
const { userProfile: { timezone } } = useAppContext()
|
|
|
|
const {
|
|
strategy_setting,
|
|
upgrade_time_of_day,
|
|
upgrade_mode,
|
|
exclude_plugins,
|
|
include_plugins,
|
|
} = payload
|
|
|
|
const minuteFilter = useCallback((minutes: string[]) => {
|
|
return minutes.filter((m) => {
|
|
const time = Number.parseInt(m, 10)
|
|
return time % 15 === 0
|
|
})
|
|
}, [])
|
|
const strategyDescription = useMemo(() => {
|
|
switch (strategy_setting) {
|
|
case AUTO_UPDATE_STRATEGY.fixOnly:
|
|
return t(`${i18nPrefix}.strategy.fixOnly.selectedDescription`, { ns: 'plugin' })
|
|
case AUTO_UPDATE_STRATEGY.latest:
|
|
return t(`${i18nPrefix}.strategy.latest.selectedDescription`, { ns: 'plugin' })
|
|
default:
|
|
return ''
|
|
}
|
|
}, [strategy_setting, t])
|
|
|
|
const plugins = useMemo(() => {
|
|
switch (upgrade_mode) {
|
|
case AUTO_UPDATE_MODE.partial:
|
|
return include_plugins
|
|
case AUTO_UPDATE_MODE.exclude:
|
|
return exclude_plugins
|
|
default:
|
|
return []
|
|
}
|
|
}, [upgrade_mode, exclude_plugins, include_plugins])
|
|
const scopeOptions = useMemo(() => [
|
|
{
|
|
value: AUTO_UPDATE_MODE.update_all,
|
|
label: t(`${i18nPrefix}.upgradeMode.${AUTO_UPDATE_MODE.update_all}`, { ns: 'plugin' }),
|
|
},
|
|
{
|
|
value: AUTO_UPDATE_MODE.exclude,
|
|
label: t(`${i18nPrefix}.upgradeMode.${AUTO_UPDATE_MODE.exclude}`, { ns: 'plugin' }),
|
|
},
|
|
{
|
|
value: AUTO_UPDATE_MODE.partial,
|
|
label: t(`${i18nPrefix}.upgradeMode.${AUTO_UPDATE_MODE.partial}`, { ns: 'plugin' }),
|
|
},
|
|
], [t])
|
|
|
|
const handlePluginsChange = useCallback((newPlugins: string[]) => {
|
|
if (upgrade_mode === AUTO_UPDATE_MODE.partial) {
|
|
onChange({
|
|
...payload,
|
|
include_plugins: newPlugins,
|
|
})
|
|
}
|
|
else if (upgrade_mode === AUTO_UPDATE_MODE.exclude) {
|
|
onChange({
|
|
...payload,
|
|
exclude_plugins: newPlugins,
|
|
})
|
|
}
|
|
}, [payload, upgrade_mode, onChange])
|
|
const handleChange = useCallback((key: keyof AutoUpdateConfig) => {
|
|
return (value: AutoUpdateConfig[keyof AutoUpdateConfig]) => {
|
|
onChange({
|
|
...payload,
|
|
[key]: value,
|
|
})
|
|
}
|
|
}, [payload, onChange])
|
|
|
|
const renderTimePickerTrigger = useCallback(({ inputElem, onClick, isOpen }: TriggerParams) => {
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="group flex h-8 w-[160px] cursor-pointer items-center justify-between rounded-lg border-none bg-components-input-bg-normal px-2 text-left hover:bg-state-base-hover-alt focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
|
|
onClick={onClick}
|
|
>
|
|
<div className="flex w-0 grow items-center gap-x-1">
|
|
<RiTimeLine className={cn(
|
|
'size-4 shrink-0 text-text-tertiary',
|
|
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
|
)}
|
|
/>
|
|
{inputElem}
|
|
</div>
|
|
<div className="system-sm-regular text-text-tertiary">{convertTimezoneToOffsetStr(timezone)}</div>
|
|
</button>
|
|
)
|
|
}, [timezone])
|
|
|
|
return (
|
|
<div className="self-stretch px-6">
|
|
<div className="my-3 flex items-center">
|
|
<div className="system-xs-medium-uppercase text-text-tertiary">{t(`${i18nPrefix}.updateSettings`, { ns: 'plugin' })}</div>
|
|
<div className="ml-2 h-px grow bg-divider-subtle"></div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<Label label={t(`${i18nPrefix}.automaticUpdates`, { ns: 'plugin' })} description={strategyDescription} />
|
|
<StrategyPicker value={strategy_setting} onChange={handleChange('strategy_setting')} />
|
|
</div>
|
|
{strategy_setting !== AUTO_UPDATE_STRATEGY.disabled && (
|
|
<>
|
|
<div className="flex items-center justify-between">
|
|
<Label label={t(`${i18nPrefix}.updateTime`, { ns: 'plugin' })} />
|
|
<div className="flex flex-col items-end">
|
|
<TimePicker
|
|
value={timeOfDayToDayjs(convertUTCDaySecondsToLocalSeconds(upgrade_time_of_day, timezone!))}
|
|
timezone={timezone}
|
|
onChange={v => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(dayjsToTimeOfDay(v), timezone!))}
|
|
onClear={() => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(0, timezone!))}
|
|
title={t(`${i18nPrefix}.updateTime`, { ns: 'plugin' })}
|
|
minuteFilter={minuteFilter}
|
|
renderTrigger={renderTimePickerTrigger}
|
|
placement="bottom-end"
|
|
/>
|
|
<div className="mt-1 text-right body-xs-regular text-text-tertiary">
|
|
<Trans
|
|
i18nKey={`${i18nPrefix}.changeTimezone`}
|
|
ns="plugin"
|
|
components={{
|
|
setTimezone: <SettingTimeZone />,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<Label label={t(`${i18nPrefix}.specifyPluginsToUpdate`, { ns: 'plugin' })} />
|
|
<ToggleGroup<AUTO_UPDATE_MODE>
|
|
aria-label={t(`${i18nPrefix}.specifyPluginsToUpdate`, { ns: 'plugin' })}
|
|
className="mt-1 flex w-full"
|
|
value={[upgrade_mode]}
|
|
onValueChange={(nextValue) => {
|
|
const selectedValue = nextValue[0]
|
|
if (selectedValue)
|
|
handleChange('upgrade_mode')(selectedValue)
|
|
}}
|
|
>
|
|
{scopeOptions.map(option => (
|
|
<ToggleGroupItem<AUTO_UPDATE_MODE>
|
|
key={option.value}
|
|
value={option.value}
|
|
className="flex-1 hover:bg-state-base-hover-alt data-pressed:text-text-accent-light-mode-only data-pressed:hover:bg-components-segmented-control-item-active-bg"
|
|
>
|
|
<span className="p-0.5 whitespace-nowrap">{option.label}</span>
|
|
</ToggleGroupItem>
|
|
))}
|
|
</ToggleGroup>
|
|
|
|
{upgrade_mode !== AUTO_UPDATE_MODE.update_all && (
|
|
<PluginsPicker
|
|
value={plugins}
|
|
onChange={handlePluginsChange}
|
|
updateMode={upgrade_mode}
|
|
/>
|
|
)}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
export default React.memo(AutoUpdateSetting)
|