選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

index.tsx 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useMemo } from 'react'
  4. import { AUTO_UPDATE_MODE, AUTO_UPDATE_STRATEGY, type AutoUpdateConfig } from './types'
  5. import Label from '../label'
  6. import StrategyPicker from './strategy-picker'
  7. import { Trans, useTranslation } from 'react-i18next'
  8. import TimePicker from '@/app/components/base/date-and-time-picker/time-picker'
  9. import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
  10. import PluginsPicker from './plugins-picker'
  11. import { convertLocalSecondsToUTCDaySeconds, convertUTCDaySecondsToLocalSeconds, dayjsToTimeOfDay, timeOfDayToDayjs } from './utils'
  12. import { useAppContext } from '@/context/app-context'
  13. import type { TriggerParams } from '@/app/components/base/date-and-time-picker/types'
  14. import { RiTimeLine } from '@remixicon/react'
  15. import cn from '@/utils/classnames'
  16. import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs'
  17. import { useModalContextSelector } from '@/context/modal-context'
  18. const i18nPrefix = 'plugin.autoUpdate'
  19. type Props = {
  20. payload: AutoUpdateConfig
  21. onChange: (payload: AutoUpdateConfig) => void
  22. }
  23. const SettingTimeZone: FC<{
  24. children?: React.ReactNode
  25. }> = ({
  26. children,
  27. }) => {
  28. const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
  29. return (
  30. <span className='body-xs-regular cursor-pointer text-text-accent' onClick={() => setShowAccountSettingModal({ payload: 'language' })} >{children}</span>
  31. )
  32. }
  33. const AutoUpdateSetting: FC<Props> = ({
  34. payload,
  35. onChange,
  36. }) => {
  37. const { t } = useTranslation()
  38. const { userProfile: { timezone } } = useAppContext()
  39. const {
  40. strategy_setting,
  41. upgrade_time_of_day,
  42. upgrade_mode,
  43. exclude_plugins,
  44. include_plugins,
  45. } = payload
  46. const minuteFilter = useCallback((minutes: string[]) => {
  47. return minutes.filter((m) => {
  48. const time = Number.parseInt(m, 10)
  49. return time % 15 === 0
  50. })
  51. }, [])
  52. const strategyDescription = useMemo(() => {
  53. switch (strategy_setting) {
  54. case AUTO_UPDATE_STRATEGY.fixOnly:
  55. return t(`${i18nPrefix}.strategy.fixOnly.selectedDescription`)
  56. case AUTO_UPDATE_STRATEGY.latest:
  57. return t(`${i18nPrefix}.strategy.latest.selectedDescription`)
  58. default:
  59. return ''
  60. }
  61. }, [strategy_setting, t])
  62. const plugins = useMemo(() => {
  63. switch (upgrade_mode) {
  64. case AUTO_UPDATE_MODE.partial:
  65. return include_plugins
  66. case AUTO_UPDATE_MODE.exclude:
  67. return exclude_plugins
  68. default:
  69. return []
  70. }
  71. }, [upgrade_mode, exclude_plugins, include_plugins])
  72. const handlePluginsChange = useCallback((newPlugins: string[]) => {
  73. if (upgrade_mode === AUTO_UPDATE_MODE.partial) {
  74. onChange({
  75. ...payload,
  76. include_plugins: newPlugins,
  77. })
  78. }
  79. else if (upgrade_mode === AUTO_UPDATE_MODE.exclude) {
  80. onChange({
  81. ...payload,
  82. exclude_plugins: newPlugins,
  83. })
  84. }
  85. }, [payload, upgrade_mode, onChange])
  86. const handleChange = useCallback((key: keyof AutoUpdateConfig) => {
  87. return (value: AutoUpdateConfig[keyof AutoUpdateConfig]) => {
  88. onChange({
  89. ...payload,
  90. [key]: value,
  91. })
  92. }
  93. }, [payload, onChange])
  94. const renderTimePickerTrigger = useCallback(({ inputElem, onClick, isOpen }: TriggerParams) => {
  95. return (
  96. <div
  97. className='group float-right flex h-8 w-[160px] cursor-pointer items-center justify-between rounded-lg bg-components-input-bg-normal px-2 hover:bg-state-base-hover-alt'
  98. onClick={onClick}
  99. >
  100. <div className='flex w-0 grow items-center gap-x-1'>
  101. <RiTimeLine className={cn(
  102. 'h-4 w-4 shrink-0 text-text-tertiary',
  103. isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
  104. )} />
  105. {inputElem}
  106. </div>
  107. <div className='system-sm-regular text-text-tertiary'>{convertTimezoneToOffsetStr(timezone)}</div>
  108. </div>
  109. )
  110. }, [timezone])
  111. return (
  112. <div className='self-stretch px-6'>
  113. <div className='my-3 flex items-center'>
  114. <div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.updateSettings`)}</div>
  115. <div className='ml-2 h-px grow bg-divider-subtle'></div>
  116. </div>
  117. <div className='space-y-4'>
  118. <div className='flex items-center justify-between'>
  119. <Label label={t(`${i18nPrefix}.automaticUpdates`)} description={strategyDescription} />
  120. <StrategyPicker value={strategy_setting} onChange={handleChange('strategy_setting')} />
  121. </div>
  122. {strategy_setting !== AUTO_UPDATE_STRATEGY.disabled && (
  123. <>
  124. <div className='flex items-center justify-between'>
  125. <Label label={t(`${i18nPrefix}.updateTime`)} />
  126. <div className='flex flex-col justify-start'>
  127. <TimePicker
  128. value={timeOfDayToDayjs(convertUTCDaySecondsToLocalSeconds(upgrade_time_of_day, timezone!))}
  129. timezone={timezone}
  130. onChange={v => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(dayjsToTimeOfDay(v), timezone!))}
  131. onClear={() => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(0, timezone!))}
  132. popupClassName='z-[99]'
  133. title={t(`${i18nPrefix}.updateTime`)}
  134. minuteFilter={minuteFilter}
  135. renderTrigger={renderTimePickerTrigger}
  136. />
  137. <div className='body-xs-regular mt-1 text-right text-text-tertiary'>
  138. <Trans
  139. i18nKey={`${i18nPrefix}.changeTimezone`}
  140. components={{
  141. setTimezone: <SettingTimeZone />,
  142. }}
  143. />
  144. </div>
  145. </div>
  146. </div>
  147. <div>
  148. <Label label={t(`${i18nPrefix}.specifyPluginsToUpdate`)} />
  149. <div className='mt-1 flex w-full items-start justify-between gap-2'>
  150. {[AUTO_UPDATE_MODE.update_all, AUTO_UPDATE_MODE.exclude, AUTO_UPDATE_MODE.partial].map(option => (
  151. <OptionCard
  152. key={option}
  153. title={t(`${i18nPrefix}.upgradeMode.${option}`)}
  154. onSelect={() => handleChange('upgrade_mode')(option)}
  155. selected={upgrade_mode === option}
  156. className="flex-1"
  157. />
  158. ))}
  159. </div>
  160. {upgrade_mode !== AUTO_UPDATE_MODE.update_all && (
  161. <PluginsPicker
  162. value={plugins}
  163. onChange={handlePluginsChange}
  164. updateMode={upgrade_mode}
  165. />
  166. )}
  167. </div>
  168. </>
  169. )}
  170. </div>
  171. </div>
  172. )
  173. }
  174. export default React.memo(AutoUpdateSetting)