'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 { SegmentedControl, SegmentedControlItem } from '@langgenius/dify-ui/segmented-control' import { RiTimeLine } from '@remixicon/react' import { useQuery } from '@tanstack/react-query' 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 { useModalContextSelector } from '@/context/modal-context' import { userProfileQueryOptions } from '@/features/account-profile/client' 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 = Readonly<{ payload: AutoUpdateConfig onChange: (payload: AutoUpdateConfig) => void }> const SettingTimeZone: FC<{ children?: React.ReactNode }> = ({ children }) => { const setShowAccountSettingModal = useModalContextSelector((s) => s.setShowAccountSettingModal) return ( setShowAccountSettingModal({ payload: ACCOUNT_SETTING_TAB.PREFERENCES })} > {children} ) } const AutoUpdateSetting: FC = ({ payload, onChange }) => { const { t } = useTranslation() const { data: timezone } = useQuery({ ...userProfileQueryOptions(), select: (data) => data.profile.timezone ?? undefined, }) 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 ( {inputElem} {convertTimezoneToOffsetStr(timezone)} ) }, [timezone], ) return ( {t(($) => $[`${i18nPrefix}.updateSettings`], { ns: 'plugin' })} $[`${i18nPrefix}.automaticUpdates`], { ns: 'plugin' })} description={strategyDescription} /> {strategy_setting !== AUTO_UPDATE_STRATEGY.disabled && ( <> $[`${i18nPrefix}.updateTime`], { ns: 'plugin' })} /> 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" /> $[`${i18nPrefix}.changeTimezone`]} ns="plugin" components={{ setTimezone: , }} /> $[`${i18nPrefix}.specifyPluginsToUpdate`], { ns: 'plugin' })} /> 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) => ( 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" > {option.label} ))} {upgrade_mode !== AUTO_UPDATE_MODE.update_all && ( )} > )} ) } export default React.memo(AutoUpdateSetting)