@electerm/electerm-react 2.3.198 → 2.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/client/common/clipboard.js +1 -1
  2. package/client/common/constants.js +2 -2
  3. package/client/common/download.jsx +3 -2
  4. package/client/common/error-handler.jsx +5 -9
  5. package/client/common/fetch-from-server.js +1 -1
  6. package/client/common/fetch.jsx +5 -5
  7. package/client/common/icon-helpers.jsx +16 -0
  8. package/client/common/parse-json-safe.js +1 -1
  9. package/client/common/pre.js +0 -7
  10. package/client/common/sftp.js +1 -1
  11. package/client/common/terminal-theme.js +1 -1
  12. package/client/common/transfer.js +2 -2
  13. package/client/common/upgrade.js +2 -2
  14. package/client/components/ai/ai-chat.jsx +10 -1
  15. package/client/components/auth/login.jsx +1 -1
  16. package/client/components/bg/css-overwrite.jsx +1 -1
  17. package/client/components/bookmark-form/form-renderer.jsx +3 -2
  18. package/client/components/common/input-auto-focus.jsx +1 -1
  19. package/client/components/common/message.jsx +131 -0
  20. package/client/components/common/message.styl +58 -0
  21. package/client/components/common/modal.jsx +176 -0
  22. package/client/components/common/modal.styl +22 -0
  23. package/client/components/common/notification-with-details.jsx +1 -1
  24. package/client/components/common/notification.jsx +94 -0
  25. package/client/components/common/notification.styl +51 -0
  26. package/client/components/main/connection-hopping-warnning.jsx +1 -3
  27. package/client/components/main/error-wrapper.jsx +3 -2
  28. package/client/components/main/main.jsx +4 -11
  29. package/client/components/main/upgrade.jsx +6 -4
  30. package/client/components/profile/profile-form-elem.jsx +1 -1
  31. package/client/components/quick-commands/quick-commands-box.jsx +5 -2
  32. package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -1
  33. package/client/components/rdp/rdp-session.jsx +2 -2
  34. package/client/components/session/session.jsx +4 -9
  35. package/client/components/setting-panel/deep-link-control.jsx +2 -1
  36. package/client/components/setting-panel/keyword-input.jsx +60 -0
  37. package/client/components/setting-panel/keywords-form.jsx +2 -7
  38. package/client/components/setting-panel/setting-common.jsx +1 -1
  39. package/client/components/setting-panel/setting-terminal.jsx +1 -1
  40. package/client/components/setting-panel/tab-settings.jsx +1 -1
  41. package/client/components/setting-sync/setting-sync-form.jsx +53 -3
  42. package/client/components/setting-sync/setting-sync.jsx +2 -1
  43. package/client/components/sftp/owner-list.js +6 -6
  44. package/client/components/sftp/sftp-entry.jsx +6 -4
  45. package/client/components/shortcuts/shortcut-editor.jsx +2 -2
  46. package/client/components/ssh-config/ssh-config-load-notify.jsx +3 -2
  47. package/client/components/tabs/tab.jsx +1 -1
  48. package/client/components/tabs/workspace-save-modal.jsx +2 -1
  49. package/client/components/terminal/attach-addon-custom.js +142 -26
  50. package/client/components/terminal/command-tracker-addon.js +164 -53
  51. package/client/components/terminal/highlight-addon.js +84 -43
  52. package/client/components/terminal/shell.js +138 -0
  53. package/client/components/terminal/terminal-command-dropdown.jsx +3 -0
  54. package/client/components/terminal/terminal.jsx +165 -103
  55. package/client/components/theme/theme-form.jsx +2 -1
  56. package/client/components/tree-list/bookmark-transport.jsx +27 -5
  57. package/client/components/vnc/vnc-session.jsx +1 -1
  58. package/client/components/widgets/widget-notification-with-details.jsx +1 -1
  59. package/client/store/common.js +5 -2
  60. package/client/store/db-upgrade.js +1 -1
  61. package/client/store/init-state.js +2 -1
  62. package/client/store/load-data.js +2 -2
  63. package/client/store/mcp-handler.js +9 -50
  64. package/client/store/setting.js +1 -3
  65. package/client/store/store.js +2 -1
  66. package/client/store/sync.js +14 -8
  67. package/client/store/system-menu.js +2 -1
  68. package/client/store/tab.js +1 -1
  69. package/client/store/widgets.js +1 -3
  70. package/package.json +1 -1
  71. package/client/common/track.js +0 -7
  72. package/client/components/batch-op/batch-op-entry.jsx +0 -13
@@ -75,3 +75,25 @@
75
75
  padding 12px 24px
76
76
  border-top 1px solid var(--main-darker)
77
77
  text-align right
78
+
79
+ .custom-modal-footer-buttons
80
+ display flex
81
+ justify-content flex-end
82
+ gap 8px
83
+
84
+ .custom-modal-ok-btn,
85
+ .custom-modal-cancel-btn
86
+ padding 6px 16px
87
+ border none
88
+ border-radius 4px
89
+ cursor pointer
90
+ font-size 14px
91
+ transition background-color 0.2s
92
+
93
+ .custom-modal-ok-btn
94
+ background var(--primary)
95
+ color var(--primary-contrast)
96
+
97
+ .custom-modal-cancel-btn
98
+ background var(--main)
99
+ color var(--text)
@@ -1,4 +1,4 @@
1
- import { notification } from 'antd'
1
+ import { notification } from './notification'
2
2
  import { CopyOutlined } from '@ant-design/icons'
3
3
  import { copy } from '../../common/clipboard'
4
4
 
@@ -0,0 +1,94 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import { CloseOutlined } from '@ant-design/icons'
3
+ import classnames from 'classnames'
4
+ import generateId from '../../common/uid'
5
+ import { messageIcons } from '../../common/icon-helpers.jsx'
6
+ import './notification.styl'
7
+
8
+ const notifications = []
9
+ const NOTIFICATION_EVENT = 'notification-update'
10
+
11
+ function addNotification (notif) {
12
+ notifications.push(notif)
13
+ window.dispatchEvent(new CustomEvent(NOTIFICATION_EVENT))
14
+ }
15
+
16
+ function removeNotification (key) {
17
+ const index = notifications.findIndex(n => n.key === key)
18
+ if (index > -1) {
19
+ notifications.splice(index, 1)
20
+ window.dispatchEvent(new CustomEvent(NOTIFICATION_EVENT))
21
+ }
22
+ }
23
+
24
+ export const notification = {
25
+ success: (options) => {
26
+ addNotification({ ...options, type: 'success', key: options.key || generateId() })
27
+ },
28
+ error: (options) => {
29
+ addNotification({ ...options, type: 'error', key: options.key || generateId() })
30
+ },
31
+ warning: (options) => {
32
+ addNotification({ ...options, type: 'warning', key: options.key || generateId() })
33
+ },
34
+ info: (options) => {
35
+ addNotification({ ...options, type: 'info', key: options.key || generateId() })
36
+ },
37
+ destroy: (key) => {
38
+ if (key) {
39
+ removeNotification(key)
40
+ } else {
41
+ notifications.length = 0
42
+ window.dispatchEvent(new CustomEvent(NOTIFICATION_EVENT))
43
+ }
44
+ }
45
+ }
46
+
47
+ export function NotificationContainer () {
48
+ const [nots, setNots] = useState([...notifications])
49
+
50
+ useEffect(() => {
51
+ const handler = () => setNots([...notifications])
52
+ window.addEventListener(NOTIFICATION_EVENT, handler)
53
+ return () => window.removeEventListener(NOTIFICATION_EVENT, handler)
54
+ }, [])
55
+
56
+ return (
57
+ <div className='notification-container'>
58
+ {nots.map(notif => (
59
+ <NotificationItem
60
+ key={notif.key}
61
+ message={notif.message}
62
+ description={notif.description}
63
+ type={notif.type}
64
+ duration={notif.duration}
65
+ onClose={() => removeNotification(notif.key)}
66
+ />
67
+ ))}
68
+ </div>
69
+ )
70
+ }
71
+
72
+ function NotificationItem ({ message, description, type, onClose, duration = 18.5 }) {
73
+ useEffect(() => {
74
+ if (duration > 0) {
75
+ const timer = setTimeout(onClose, duration * 1000)
76
+ return () => clearTimeout(timer)
77
+ }
78
+ }, [duration, onClose])
79
+
80
+ const className = classnames('notification', type)
81
+
82
+ return (
83
+ <div className={className}>
84
+ <div className='notification-content'>
85
+ <div className='notification-message'>
86
+ <div className='notification-icon'>{messageIcons[type]}</div>
87
+ <div className='notification-title' title={message}>{message}</div>
88
+ </div>
89
+ {description && <div className='notification-description'>{description}</div>}
90
+ </div>
91
+ <CloseOutlined className='notification-close' onClick={onClose} />
92
+ </div>
93
+ )
94
+ }
@@ -0,0 +1,51 @@
1
+ .notification-container
2
+ position fixed
3
+ bottom 20px
4
+ right 20px
5
+ z-index 1000
6
+
7
+ .notification
8
+ background var(--main-lighter)
9
+ color var(--text)
10
+ border-radius 4px
11
+ margin-bottom 10px
12
+ padding 12px 16px
13
+ box-shadow 0 4px 12px rgba(0, 0, 0, 0.15)
14
+ width 400px
15
+ position relative
16
+
17
+ .notification-content
18
+ width 100%
19
+
20
+ .notification-message
21
+ font-weight bold
22
+ margin-bottom 10px
23
+ .notification-title
24
+ overflow hidden
25
+ text-overflow ellipsis
26
+ white-space nowrap
27
+ padding 0 30px 0 20px
28
+
29
+ .notification-icon
30
+ font-size 16px
31
+ position absolute
32
+ left 12px
33
+ top 10px
34
+
35
+ .notification-description
36
+ word-wrap break-all
37
+ max-height 200px
38
+ overflow auto
39
+
40
+ .notification-close
41
+ position absolute
42
+ top 12px
43
+ right 12px
44
+ background none
45
+ border none
46
+ color var(--text-dark)
47
+ font-size 18px
48
+ cursor pointer
49
+ padding 0
50
+ &:hover
51
+ color var(--text)
@@ -1,8 +1,6 @@
1
1
  import { useEffect } from 'react'
2
2
  import ConnectionHoppingWarningText from '../common/connection-hopping-warning-text'
3
- import {
4
- notification
5
- } from 'antd'
3
+ import { notification } from '../common/notification'
6
4
  import * as ls from '../../common/safe-local-storage'
7
5
  import {
8
6
  connectionHoppingWarnKey
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import { FrownOutlined, ReloadOutlined } from '@ant-design/icons'
3
- import { Button, message } from 'antd'
3
+ import { Button } from 'antd'
4
+ import message from '../common/message'
4
5
  import {
5
6
  logoPath1,
6
7
  packInfo,
@@ -41,7 +42,7 @@ export default class ErrorBoundary extends React.PureComponent {
41
42
  }
42
43
 
43
44
  componentDidCatch (error) {
44
- log.error(error)
45
+ console.error(error)
45
46
  this.setState({
46
47
  hasError: true,
47
48
  error
@@ -6,7 +6,7 @@ import UpdateCheck from './upgrade'
6
6
  import SettingModal from '../setting-panel/setting-modal'
7
7
  import TextEditor from '../text-editor/text-editor'
8
8
  import Sidebar from '../sidebar'
9
- import BatchOp from '../batch-op/batch-op-entry'
9
+ import BatchOp from '../batch-op/batch-op'
10
10
  import CssOverwrite from '../bg/css-overwrite'
11
11
  import UiTheme from './ui-theme'
12
12
  import CustomCss from '../bg/custom-css.jsx'
@@ -21,7 +21,8 @@ import ShortcutControl from '../shortcuts/shortcut-control.jsx'
21
21
  import { isMac, isWin, textTerminalBgValue } from '../../common/constants'
22
22
  import TermFullscreenControl from './term-fullscreen-control'
23
23
  import TerminalInfo from '../terminal-info/terminal-info'
24
- import { ConfigProvider, notification, message } from 'antd'
24
+ import { ConfigProvider } from 'antd'
25
+ import { NotificationContainer } from '../common/notification'
25
26
  import InfoModal from '../sidebar/info-modal.jsx'
26
27
  import RightSidePanel from '../side-panel-r/side-panel-r'
27
28
  import ConnectionHoppingWarning from './connection-hopping-warnning'
@@ -37,19 +38,10 @@ import deepCopy from 'json-deep-copy'
37
38
  import './wrapper.styl'
38
39
 
39
40
  function setupGlobalMessageDismiss () {
40
- document.addEventListener('click', (event) => {
41
- const messageElement = event.target.closest('.ant-message-notice')
42
- if (messageElement) {
43
- message.destroy()
44
- }
45
- })
46
41
  }
47
42
 
48
43
  export default auto(function Index (props) {
49
44
  useEffect(() => {
50
- notification.config({
51
- placement: 'bottomRight'
52
- })
53
45
  setupGlobalMessageDismiss()
54
46
  const { store } = props
55
47
  window.addEventListener('resize', store.onResize)
@@ -307,6 +299,7 @@ export default auto(function Index (props) {
307
299
  <TerminalCmdSuggestions {...cmdSuggestionsProps} />
308
300
  <TransferQueue />
309
301
  <WorkspaceSaveModal store={store} />
302
+ <NotificationContainer />
310
303
  </div>
311
304
  </ConfigProvider>
312
305
  )
@@ -37,9 +37,6 @@ export default class Upgrade extends PureComponent {
37
37
  if (window.et.isWebApp) {
38
38
  return
39
39
  }
40
- setTimeout(() => {
41
- getLatestReleaseVersion(1)
42
- }, 5000)
43
40
  this.id = 'upgrade'
44
41
  refsStatic.add(this.id, this)
45
42
  }
@@ -119,7 +116,12 @@ export default class Upgrade extends PureComponent {
119
116
  return window.store.addTab(
120
117
  {
121
118
  ...newTerm(undefined, true),
122
- loginScript: 'npm i -g electerm'
119
+ runScripts: [
120
+ {
121
+ script: 'npm install -g electerm',
122
+ delay: 500
123
+ }
124
+ ]
123
125
  }
124
126
  )
125
127
  }
@@ -1,10 +1,10 @@
1
1
  import { useState } from 'react'
2
2
  import {
3
3
  Form,
4
- message,
5
4
  Switch,
6
5
  Button
7
6
  } from 'antd'
7
+ import message from '../common/message'
8
8
  import InputAutoFocus from '../common/input-auto-focus'
9
9
  import { formItemLayout } from '../../common/form-layout'
10
10
  import HelpIcon from '../common/help-icon'
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { useState, useRef } from 'react'
6
- import { quickCommandLabelsLsKey } from '../../common/constants'
6
+ import { quickCommandLabelsLsKey, pinnedQuickCommandBarKey } from '../../common/constants'
7
7
  import { sortBy } from 'lodash-es'
8
8
  import { Button, Input, Select, Space, Flex } from 'antd'
9
9
  import * as ls from '../../common/safe-local-storage'
@@ -41,7 +41,9 @@ export default function QuickCommandsFooterBox (props) {
41
41
  }
42
42
 
43
43
  function handleTogglePinned () {
44
- window.store.pinnedQuickCommandBar = !window.store.pinnedQuickCommandBar
44
+ const current = !window.store.pinnedQuickCommandBar
45
+ ls.setItem(pinnedQuickCommandBarKey, current ? 'y' : 'n')
46
+ window.store.pinnedQuickCommandBar = current
45
47
  }
46
48
 
47
49
  async function handleSelect (id) {
@@ -56,6 +58,7 @@ export default function QuickCommandsFooterBox (props) {
56
58
  }
57
59
 
58
60
  function handleClose () {
61
+ ls.setItem(pinnedQuickCommandBarKey, 'n')
59
62
  window.store.pinnedQuickCommandBar = false
60
63
  window.store.openQuickCommandBar = false
61
64
  }
@@ -2,10 +2,10 @@ import {
2
2
  Button,
3
3
  Switch,
4
4
  Form,
5
- message,
6
5
  Select,
7
6
  Input
8
7
  } from 'antd'
8
+ import message from '../common/message'
9
9
  import { useState } from 'react'
10
10
  import generate from '../../common/uid'
11
11
  import InputAutoFocus from '../common/input-auto-focus'
@@ -7,11 +7,11 @@ import {
7
7
  statusMap
8
8
  } from '../../common/constants'
9
9
  import {
10
- notification,
11
10
  Spin,
12
11
  // Button,
13
12
  Select
14
13
  } from 'antd'
14
+ import { notification } from '../common/notification'
15
15
  import {
16
16
  ReloadOutlined,
17
17
  EditOutlined
@@ -246,7 +246,7 @@ export default class RdpSession extends PureComponent {
246
246
 
247
247
  onerrorSocket = err => {
248
248
  this.setStatus(statusMap.error)
249
- log.error('socket error', err)
249
+ console.error('socket error', err)
250
250
  }
251
251
 
252
252
  closeMsg = () => {
@@ -17,7 +17,6 @@ import {
17
17
  } from '@ant-design/icons'
18
18
  import {
19
19
  Tooltip,
20
- message,
21
20
  Splitter
22
21
  } from 'antd'
23
22
  import { pick } from 'lodash-es'
@@ -206,13 +205,9 @@ export default class SessionWrapper extends Component {
206
205
  }
207
206
 
208
207
  toggleCheckSftpPathFollowSsh = () => {
209
- const nv = !this.state.sftpPathFollowSsh
210
- if (nv) {
211
- message.warning(e('sftpPathFollowSshTip'), 8)
212
- }
213
- this.setState({
214
- sftpPathFollowSsh: nv
215
- })
208
+ this.setState(prevState => ({
209
+ sftpPathFollowSsh: !prevState.sftpPathFollowSsh
210
+ }))
216
211
  }
217
212
 
218
213
  editTab = (up) => {
@@ -660,7 +655,7 @@ export default class SessionWrapper extends Component {
660
655
  const termType = tab?.type
661
656
  const isSsh = tab.authType
662
657
  const isLocal = !isSsh && (termType === connectionMap.local || !termType)
663
- const checkTxt = e('sftpPathFollowSsh') + ' [Beta]'
658
+ const checkTxt = e('sftpPathFollowSsh')
664
659
  const checkProps = {
665
660
  onClick: this.toggleCheckSftpPathFollowSsh,
666
661
  className: classnames(
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useEffect } from 'react'
2
- import { Button, message, Tooltip, Tag, Space } from 'antd'
2
+ import { Button, Tooltip, Tag, Space } from 'antd'
3
+ import message from '../common/message'
3
4
  import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'
4
5
 
5
6
  const e = window.translate
@@ -0,0 +1,60 @@
1
+ import { Input } from 'antd'
2
+ import { CheckOutlined, CloseOutlined } from '@ant-design/icons'
3
+ import { useState, useEffect } from 'react'
4
+
5
+ export default function KeywordInput (props) {
6
+ const { value, onChange, addonBefore, ...rest } = props
7
+ const [localValue, setLocalValue] = useState(value || '')
8
+ const [isEditing, setIsEditing] = useState(false)
9
+
10
+ useEffect(() => {
11
+ setLocalValue(value || '')
12
+ }, [value])
13
+
14
+ function handleChange (e) {
15
+ const newValue = e.target.value
16
+ setLocalValue(newValue)
17
+ setIsEditing(true)
18
+ }
19
+
20
+ function handleConfirm () {
21
+ onChange(localValue)
22
+ setIsEditing(false)
23
+ }
24
+
25
+ function handleCancel () {
26
+ setLocalValue(value || '')
27
+ setIsEditing(false)
28
+ }
29
+
30
+ function handleBlur () {
31
+ // Optionally hide icons on blur, but since user might click icons, maybe not
32
+ // setIsEditing(false)
33
+ }
34
+
35
+ const addonAfter = isEditing
36
+ ? (
37
+ <div>
38
+ <CheckOutlined
39
+ onClick={handleConfirm}
40
+ className='mg1r pointer'
41
+ />
42
+ <CloseOutlined
43
+ onClick={handleCancel}
44
+ className='pointer'
45
+ />
46
+ </div>
47
+ )
48
+ : null
49
+
50
+ return (
51
+ <Input
52
+ value={localValue}
53
+ onChange={handleChange}
54
+ onBlur={handleBlur}
55
+ addonBefore={addonBefore}
56
+ addonAfter={addonAfter}
57
+ {...rest}
58
+ />
59
+ )
60
+ }
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  Form,
3
- Input,
4
3
  Select,
5
4
  Space,
6
5
  Button
@@ -11,6 +10,7 @@ import {
11
10
  PlusOutlined
12
11
  } from '@ant-design/icons'
13
12
  import { useEffect } from 'react'
13
+ import KeywordInput from './keyword-input'
14
14
 
15
15
  const FormItem = Form.Item
16
16
  const FormList = Form.List
@@ -26,10 +26,6 @@ export default function KeywordForm (props) {
26
26
  formChild.submit()
27
27
  }
28
28
 
29
- function handleChange (e) {
30
- formChild.submit()
31
- }
32
-
33
29
  function handleFinish (data) {
34
30
  props.submit(data)
35
31
  }
@@ -59,9 +55,8 @@ export default function KeywordForm (props) {
59
55
  name={[field.name, 'keyword']}
60
56
  rules={[{ validator: checker }]}
61
57
  >
62
- <Input
58
+ <KeywordInput
63
59
  addonBefore={renderBefore(field.name)}
64
- onChange={handleChange}
65
60
  />
66
61
  </FormItem>
67
62
  </FormItem>
@@ -5,8 +5,8 @@ import {
5
5
  SunOutlined,
6
6
  MoonOutlined
7
7
  } from '@ant-design/icons'
8
+ import message from '../common/message'
8
9
  import {
9
- message,
10
10
  Select,
11
11
  Switch,
12
12
  Input,
@@ -3,8 +3,8 @@ import {
3
3
  CodeOutlined,
4
4
  LoadingOutlined
5
5
  } from '@ant-design/icons'
6
+ import message from '../common/message'
6
7
  import {
7
- message,
8
8
  Select,
9
9
  Switch,
10
10
  Input,
@@ -1,5 +1,5 @@
1
1
  import { auto } from 'manate/react'
2
- import { message } from 'antd'
2
+ import message from '../common/message'
3
3
  import SettingCommon from './setting-common'
4
4
  import SettingTerminal from './setting-terminal'
5
5
  import SettingCol from './col'
@@ -7,7 +7,8 @@
7
7
  */
8
8
  import { useDelta, useConditionalEffect } from 'react-delta-hooks'
9
9
  import { ArrowDownOutlined, ArrowUpOutlined, SaveOutlined, ClearOutlined } from '@ant-design/icons'
10
- import { Button, Input, notification, Form } from 'antd'
10
+ import { Button, Input, Form, Alert } from 'antd'
11
+ import { notification } from '../common/notification'
11
12
  import Link from '../common/external-link'
12
13
  import dayjs from 'dayjs'
13
14
  import eq from 'fast-deep-equal'
@@ -15,6 +16,7 @@ import { syncTokenCreateUrls, syncTypes } from '../../common/constants'
15
16
  import HelpIcon from '../common/help-icon'
16
17
  import ServerDataStatus from './server-data-status'
17
18
  import Password from '../common/password'
19
+ import { isError } from 'lodash-es'
18
20
 
19
21
  const FormItem = Form.Item
20
22
  const e = window.translate
@@ -58,11 +60,17 @@ export default function SyncForm (props) {
58
60
  up[syncType + 'ApiUrl'] = 'https://electerm-cloud.html5beta.com/api/sync'
59
61
  // up[syncType + 'ApiUrl'] = 'http://127.0.0.1:5678/api/sync'
60
62
  }
63
+ if (res.proxy) {
64
+ up[syncType + 'Proxy'] = res.proxy
65
+ } else {
66
+ up[syncType + 'Proxy'] = ''
67
+ }
61
68
  window.store.updateSyncSetting(up)
62
69
  const test = await window.store.testSyncToken(syncType, res.gistId)
63
- if (!test) {
70
+ if (isError(test)) {
64
71
  return notification.error({
65
- title: 'token invalid'
72
+ message: test.message || 'Request failed',
73
+ description: test.stack || 'Request failed'
66
74
  })
67
75
  }
68
76
  if (!res.gistId && syncType !== syncTypes.custom && syncType !== syncTypes.cloud) {
@@ -136,6 +144,27 @@ export default function SyncForm (props) {
136
144
  function createId (name) {
137
145
  return 'sync-input-' + name + '-' + syncType
138
146
  }
147
+ function renderWarning () {
148
+ if (syncType === syncTypes.gitee) {
149
+ return (
150
+ <Alert
151
+ message={
152
+ <span>
153
+ Gitee data sync is not recommended. For more information, please refer to the
154
+ <Link to='https://github.com/electerm/electerm/wiki/gitee-data-sync-warning' className='mg1l'>
155
+ wiki
156
+ </Link>
157
+ .
158
+ </span>
159
+ }
160
+ type='warning'
161
+ showIcon
162
+ className='mg1b'
163
+ />
164
+ )
165
+ }
166
+ return null
167
+ }
139
168
  function createUrlItem () {
140
169
  if (syncType === syncTypes.cloud) {
141
170
  return (
@@ -215,6 +244,23 @@ export default function SyncForm (props) {
215
244
  </FormItem>
216
245
  )
217
246
  }
247
+ function createProxyItem () {
248
+ return (
249
+ <FormItem
250
+ label='Proxy'
251
+ name='proxy'
252
+ normalize={trim}
253
+ rules={[{
254
+ max: 200, message: '200 chars max'
255
+ }]}
256
+ >
257
+ <Input
258
+ placeholder='socks5://127.0.0.1:1080'
259
+ id={createId('proxy')}
260
+ />
261
+ </FormItem>
262
+ )
263
+ }
218
264
  const sprops = {
219
265
  type: syncType,
220
266
  status: props.serverStatus
@@ -228,6 +274,7 @@ export default function SyncForm (props) {
228
274
  layout='vertical'
229
275
  initialValues={props.formData}
230
276
  >
277
+ {renderWarning()}
231
278
  {createUrlItem()}
232
279
  <FormItem
233
280
  label={tokenLabel}
@@ -251,6 +298,9 @@ export default function SyncForm (props) {
251
298
  {
252
299
  createPasswordItem()
253
300
  }
301
+ {
302
+ createProxyItem()
303
+ }
254
304
  <FormItem>
255
305
  <p>
256
306
  <Button
@@ -43,7 +43,8 @@ export default auto(function SyncSettingEntry (props) {
43
43
  url: syncSetting[type + 'Url'],
44
44
  apiUrl: syncSetting[type + 'ApiUrl'],
45
45
  lastSyncTime: syncSetting[type + 'LastSyncTime'],
46
- syncPassword: syncSetting[type + 'SyncPassword']
46
+ syncPassword: syncSetting[type + 'SyncPassword'],
47
+ proxy: syncSetting[type + 'Proxy']
47
48
  }
48
49
  return (
49
50
  <SyncForm