@electerm/electerm-react 1.60.16 → 1.60.29

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 (104) hide show
  1. package/client/common/clipboard.js +1 -14
  2. package/client/common/constants.js +0 -43
  3. package/client/common/data-compare.js +55 -0
  4. package/client/common/default-setting.js +2 -10
  5. package/client/common/resolve.js +18 -22
  6. package/client/common/sftp.js +0 -3
  7. package/client/components/ai/ai-chat.jsx +30 -6
  8. package/client/components/ai/ai-config.jsx +17 -6
  9. package/client/components/batch-op/batch-op.jsx +3 -24
  10. package/client/components/bookmark-form/bookmark-group-tree-format.js +7 -9
  11. package/client/components/bookmark-form/form-ssh-common.jsx +0 -2
  12. package/client/components/bookmark-form/ssh-form.jsx +8 -41
  13. package/client/components/bookmark-form/tree-delete.jsx +6 -15
  14. package/client/components/common/animate-text.jsx +3 -4
  15. package/client/components/common/drag-handle.jsx +59 -45
  16. package/client/components/common/drag-handle.styl +2 -1
  17. package/client/components/common/input-auto-focus.jsx +29 -63
  18. package/client/components/common/ref.js +24 -0
  19. package/client/components/footer/batch-input.jsx +1 -6
  20. package/client/components/footer/footer-entry.jsx +13 -16
  21. package/client/components/footer/footer.styl +0 -5
  22. package/client/components/icons/ai-icon.jsx +17 -0
  23. package/client/components/icons/ai-icon.styl +3 -0
  24. package/client/components/layout/layout-item.jsx +14 -0
  25. package/client/components/main/main.jsx +8 -19
  26. package/client/components/main/upgrade.jsx +13 -25
  27. package/client/components/profile/profile-form-elem.jsx +1 -2
  28. package/client/components/quick-commands/on-drop.js +1 -12
  29. package/client/components/quick-commands/quick-command-transport-mod.jsx +3 -13
  30. package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -2
  31. package/client/components/rdp/rdp-session.jsx +4 -4
  32. package/client/components/session/session.jsx +9 -11
  33. package/client/components/setting-panel/on-tree-drop.js +4 -35
  34. package/client/components/setting-panel/setting-common.jsx +4 -1
  35. package/client/components/setting-panel/setting-modal.jsx +7 -5
  36. package/client/components/setting-panel/tab-settings.jsx +0 -1
  37. package/client/components/setting-sync/setting-sync.jsx +0 -1
  38. package/client/components/sftp/address-bookmark-item.jsx +1 -15
  39. package/client/components/sftp/confirm-modal-store.jsx +2 -2
  40. package/client/components/sftp/{file-mode-modal.jsx → file-info-modal.jsx} +137 -37
  41. package/client/components/sftp/file-item.jsx +156 -192
  42. package/client/components/sftp/file-table-header.jsx +98 -0
  43. package/client/components/sftp/list-table-ui.jsx +125 -416
  44. package/client/components/sftp/sftp-entry.jsx +102 -128
  45. package/client/components/sftp/sftp.styl +6 -22
  46. package/client/components/sftp/transfer-conflict-store.jsx +8 -12
  47. package/client/components/sftp/transport-action-store.jsx +7 -15
  48. package/client/components/shortcuts/shortcut-control.jsx +72 -3
  49. package/client/components/shortcuts/shortcut-handler.js +0 -1
  50. package/client/components/side-panel-r/side-panel-r.jsx +7 -4
  51. package/client/components/sidebar/history.jsx +3 -0
  52. package/client/components/sidebar/index.jsx +1 -1
  53. package/client/components/sidebar/info-modal.jsx +3 -0
  54. package/client/components/sidebar/side-panel.jsx +7 -4
  55. package/client/components/sidebar/sidebar-panel.jsx +1 -1
  56. package/client/components/sidebar/sidebar.styl +3 -3
  57. package/client/components/sys-menu/icons-map.jsx +52 -0
  58. package/client/components/{context-menu → sys-menu}/menu-btn.jsx +33 -45
  59. package/client/components/sys-menu/sys-menu.jsx +163 -0
  60. package/client/components/{context-menu/context-menu.styl → sys-menu/sys-menu.styl} +2 -11
  61. package/client/components/tabs/index.jsx +5 -97
  62. package/client/components/tabs/tab.jsx +121 -73
  63. package/client/components/tabs/tabs.styl +4 -1
  64. package/client/components/terminal/term-search.jsx +16 -28
  65. package/client/components/terminal/terminal-interactive.jsx +0 -2
  66. package/client/components/terminal/{index.jsx → terminal.jsx} +126 -248
  67. package/client/components/terminal-info/base.jsx +21 -46
  68. package/client/components/terminal-info/terminal-info.jsx +3 -0
  69. package/client/components/text-editor/text-editor.jsx +38 -53
  70. package/client/components/theme/theme-form.jsx +0 -2
  71. package/client/components/tree-list/bookmark-toolbar.jsx +23 -47
  72. package/client/components/tree-list/bookmark-transport.jsx +2 -90
  73. package/client/components/tree-list/move-item-modal.jsx +101 -0
  74. package/client/components/tree-list/tree-list-item.jsx +6 -8
  75. package/client/components/tree-list/tree-list.jsx +48 -273
  76. package/client/components/vnc/vnc-session.jsx +5 -3
  77. package/client/store/app-upgrade.js +2 -5
  78. package/client/store/bookmark-group.js +74 -28
  79. package/client/store/common.js +36 -54
  80. package/client/store/event.js +4 -37
  81. package/client/store/init-state.js +9 -12
  82. package/client/store/item.js +34 -39
  83. package/client/store/load-data.js +5 -1
  84. package/client/store/quick-command.js +2 -12
  85. package/client/store/session.js +6 -7
  86. package/client/store/setting.js +3 -7
  87. package/client/store/sidebar.js +2 -8
  88. package/client/store/store.js +0 -20
  89. package/client/store/system-menu.js +1 -2
  90. package/client/store/tab.js +29 -1
  91. package/client/store/terminal-theme.js +0 -4
  92. package/client/store/watch.js +26 -4
  93. package/package.json +1 -1
  94. package/client/common/post-msg.js +0 -3
  95. package/client/components/common/native-input.jsx +0 -30
  96. package/client/components/context-menu/context-menu.jsx +0 -339
  97. package/client/components/sftp/file-props-modal.jsx +0 -210
  98. package/client/store/context-menu.js +0 -23
  99. /package/client/components/{context-menu → sys-menu}/boomarks.jsx +0 -0
  100. /package/client/components/{context-menu → sys-menu}/history.jsx +0 -0
  101. /package/client/components/{context-menu → sys-menu}/icon-holder.jsx +0 -0
  102. /package/client/components/{context-menu → sys-menu}/sub-tab-menu.jsx +0 -0
  103. /package/client/components/{context-menu → sys-menu}/tabs.jsx +0 -0
  104. /package/client/components/{context-menu → sys-menu}/zoom.jsx +0 -0
@@ -1,34 +1,30 @@
1
1
  import { Component } from 'react'
2
+ import { refs } from '../common/ref'
2
3
  import generate from '../../common/uid'
3
4
  import runIdle from '../../common/run-idle'
4
5
  import { Spin, Modal, notification } from 'antd'
5
- import { find, isString, isEqual, last, isNumber, some, isArray, pick, uniq, debounce } from 'lodash-es'
6
+ import { isEqual, last, isNumber, some, isArray, pick, uniq, debounce } from 'lodash-es'
6
7
  import FileSection from './file-item'
7
8
  import resolve from '../../common/resolve'
8
9
  import wait from '../../common/wait'
9
10
  import isAbsPath from '../../common/is-absolute-path'
10
11
  import classnames from 'classnames'
11
12
  import sorterIndex from '../../common/index-sorter'
12
- import { getLocalFileInfo, getRemoteFileInfo } from './file-read'
13
+ import { getLocalFileInfo, getRemoteFileInfo, getFolderFromFilePath } from './file-read'
13
14
  import {
14
15
  typeMap, maxSftpHistory, paneMap,
15
- eventTypes,
16
16
  fileTypeMap,
17
17
  terminalSerialType,
18
18
  unexpectedPacketErrorDesc,
19
- sftpRetryInterval,
20
- commonActions
19
+ sftpRetryInterval
21
20
  } from '../../common/constants'
22
21
  import { hasFileInClipboardText } from '../../common/clipboard'
23
22
  import Client from '../../common/sftp'
24
23
  import fs from '../../common/fs'
25
- import keyControlPressed from '../../common/key-control-pressed'
26
- import keyPressed from '../../common/key-pressed'
27
24
  import ListTable from './list-table-ui'
28
25
  import deepCopy from 'json-deep-copy'
29
26
  import isValidPath from '../../common/is-valid-path'
30
27
  import memoizeOne from 'memoize-one'
31
- import postMessage from '../../common/post-msg'
32
28
  import * as owner from './owner-list'
33
29
  import AddressBar from './address-bar'
34
30
  import getProxy from '../../common/get-proxy'
@@ -36,15 +32,6 @@ import './sftp.styl'
36
32
 
37
33
  const e = window.translate
38
34
 
39
- const buildTree = arr => {
40
- return arr.reduce((prev, curr) => {
41
- return {
42
- ...prev,
43
- [curr.id]: curr
44
- }
45
- }, {})
46
- }
47
-
48
35
  export default class Sftp extends Component {
49
36
  constructor (props) {
50
37
  super(props)
@@ -60,10 +47,12 @@ export default class Sftp extends Component {
60
47
  this.retryCount = 0
61
48
  }
62
49
 
63
- // componentDidMount () {
64
- // // this.initData()
65
- // // this.initEvent()
66
- // }
50
+ componentDidMount () {
51
+ this.id = 'sftp-' + this.props.sessionId
52
+ this.tid = 'sftp-' + this.props.tab.id
53
+ refs.add(this.id, this)
54
+ refs.add(this.tid, this)
55
+ }
67
56
 
68
57
  componentDidUpdate (prevProps, prevState) {
69
58
  if (
@@ -78,7 +67,6 @@ export default class Sftp extends Component {
78
67
  this.props.enableSftp !== false
79
68
  )
80
69
  ) {
81
- this.initEvent()
82
70
  this.initData(true)
83
71
  }
84
72
  if (
@@ -114,7 +102,8 @@ export default class Sftp extends Component {
114
102
  }
115
103
 
116
104
  componentWillUnmount () {
117
- this.destroyEvent()
105
+ refs.remove(this.id)
106
+ refs.remove(this.tid)
118
107
  this.sftp && this.sftp.destroy()
119
108
  this.sftp = null
120
109
  clearTimeout(this.timer4)
@@ -157,58 +146,41 @@ export default class Sftp extends Component {
157
146
  sortDirection,
158
147
  sortProp
159
148
  ) => {
160
- const l0 = find(list, g => !g.id)
161
- const l1 = list.filter(g => g.id && g.isDirectory)
162
- const l2 = list.filter(g => g.id && !g.isDirectory)
163
- const sorter = (a, b) => {
164
- let va = a[sortProp]
165
- let vb = b[sortProp]
166
- if (isString(va)) {
167
- va = va.toLowerCase()
168
- vb = vb.toLowerCase()
169
- }
170
- if (sortDirection === 'desc') {
171
- if (isString(va)) {
172
- return va.localeCompare(vb, { sensitivity: 'base' })
173
- }
174
- return va > vb ? -1 : 1
175
- } else {
176
- if (isString(va)) {
177
- return -va.localeCompare(vb, { sensitivity: 'base' })
178
- }
179
- return va > vb ? 1 : -1
180
- }
149
+ if (!list || !list.length) {
150
+ return []
181
151
  }
182
- return [
183
- l0,
184
- ...l1.sort(sorter),
185
- ...l2.sort(sorter)
186
- ].filter(d => d)
187
- }, isEqual)
188
152
 
189
- initEvent () {
190
- window.addEventListener('message', this.handleMsg)
191
- window.addEventListener('keydown', this.handleEvent)
192
- }
153
+ const isDesc = sortDirection === 'desc'
193
154
 
194
- destroyEvent () {
195
- window.removeEventListener('keydown', this.handleEvent)
196
- window.removeEventListener('message', this.handleMsg)
197
- }
155
+ return list.slice().sort((a, b) => {
156
+ // Handle items with no id first
157
+ if (!a.id && b.id) return -1
158
+ if (a.id && !b.id) return 1
159
+ if (!a.id && !b.id) return 0
198
160
 
199
- handleMsg = event => {
200
- const {
201
- action,
202
- sessionId,
203
- type
204
- } = event?.data || {}
205
- if (
206
- action === commonActions.sftpList &&
207
- sessionId === this.props.sessionId
208
- ) {
209
- this[type + 'ListDebounce']()
210
- }
211
- }
161
+ // Sort directories before files
162
+ if (a.isDirectory !== b.isDirectory) {
163
+ return a.isDirectory ? -1 : 1
164
+ }
165
+
166
+ // Sort by the specified property
167
+ let aValue = a[sortProp]
168
+ let bValue = b[sortProp]
169
+
170
+ if (typeof aValue === 'string') {
171
+ aValue = aValue.toLowerCase()
172
+ bValue = bValue.toLowerCase()
173
+ return isDesc
174
+ ? bValue.localeCompare(aValue, { sensitivity: 'base' })
175
+ : aValue.localeCompare(bValue, { sensitivity: 'base' })
176
+ }
177
+
178
+ // For non-string values, use simple comparison
179
+ if (aValue < bValue) return isDesc ? 1 : -1
180
+ if (aValue > bValue) return isDesc ? -1 : 1
181
+ return 0
182
+ })
183
+ }, isEqual)
212
184
 
213
185
  isActive () {
214
186
  return this.props.enableSftp && this.props.currentBatchTabId === this.props.tab.id &&
@@ -266,15 +238,6 @@ export default class Sftp extends Component {
266
238
  return this.getFileList(type).findIndex(f => f.id === file.id)
267
239
  }
268
240
 
269
- onResizeDragEnd = () => {
270
- postMessage({
271
- type: eventTypes.resetFileListTable,
272
- data: {
273
- id: this.state.id
274
- }
275
- })
276
- }
277
-
278
241
  selectAll = (type, e) => {
279
242
  e && e.preventDefault && e.preventDefault()
280
243
  this.setState({
@@ -344,8 +307,25 @@ export default class Sftp extends Component {
344
307
  await func(p).catch(window.store.onError)
345
308
  }
346
309
 
310
+ confirmDelete = (files) => {
311
+ return new Promise((resolve) => {
312
+ Modal.confirm({
313
+ title: this.renderDelConfirmTitle(files),
314
+ okText: e('ok'),
315
+ cancelText: e('cancel'),
316
+ onOk: () => resolve(true),
317
+ onCancel: () => resolve(false)
318
+ })
319
+ })
320
+ }
321
+
347
322
  delFiles = async (_type, files = this.state.selectedFiles) => {
323
+ this.onDelete = true
324
+ const confirm = await this.confirmDelete(files)
348
325
  this.onDelete = false
326
+ if (!confirm) {
327
+ return
328
+ }
349
329
  const type = files[0].type || _type
350
330
  const func = this[type + 'Del']
351
331
  for (const f of files) {
@@ -380,16 +360,6 @@ export default class Sftp extends Component {
380
360
  )
381
361
  }
382
362
 
383
- onDel = (type, files) => {
384
- this.onDelete = true
385
- Modal.confirm({
386
- cancelText: e('cancel'),
387
- okText: e('ok'),
388
- title: this.renderDelConfirmTitle(files),
389
- onOk: () => this.delFiles(type, files)
390
- })
391
- }
392
-
393
363
  enter = (type, e) => {
394
364
  const { selectedFiles, onEditFile } = this.state
395
365
  if (onEditFile || selectedFiles.length !== 1) {
@@ -437,41 +407,6 @@ export default class Sftp extends Component {
437
407
  this[type + 'Dom'].onPaste()
438
408
  }
439
409
 
440
- handleEvent = (e) => {
441
- if (!this.isActive() || window.store.onOperation) {
442
- return
443
- }
444
- const lastClickedFile = this.state.lastClickedFile || {
445
- type: typeMap.local
446
- }
447
- const { type } = lastClickedFile
448
- const { inputFocus, onDelete } = this
449
- e.stopPropagation()
450
- if (keyControlPressed(e) && keyPressed(e, 'keyA') && !inputFocus) {
451
- this.selectAll(type, e)
452
- } else if (keyPressed(e, 'arrowdown') && !inputFocus) {
453
- this.selectNext(type)
454
- } else if (keyPressed(e, 'arrowup') && !inputFocus) {
455
- this.selectPrev(type)
456
- } else if (
457
- keyPressed(e, 'delete') &&
458
- !inputFocus &&
459
- !this.state.onEditFile
460
- ) {
461
- this.onDel(type)
462
- } else if (keyPressed(e, 'enter') && !inputFocus && !onDelete) {
463
- this.enter(type, e)
464
- } else if (keyControlPressed(e) && keyPressed(e, 'keyC') && !inputFocus) {
465
- this.doCopy(type, e)
466
- } else if (keyControlPressed(e) && keyPressed(e, 'keyX') && !inputFocus) {
467
- this.doCut(type, e)
468
- } else if (keyControlPressed(e) && keyPressed(e, 'keyV') && !inputFocus) {
469
- this.doPaste(type, e)
470
- } else if (keyPressed(e, 'f5')) {
471
- this.onGoto(type)
472
- }
473
- }
474
-
475
410
  initData = async () => {
476
411
  if (this.shouldRenderRemote()) {
477
412
  this.initRemoteAll()
@@ -537,6 +472,18 @@ export default class Sftp extends Component {
537
472
  })
538
473
  }
539
474
 
475
+ buildTree = (arr, type) => {
476
+ const parent = this.renderParentItem(type)
477
+ const treeMap = new Map(arr.map(d => [d.id, d]))
478
+
479
+ // Only add parent if it exists
480
+ if (parent) {
481
+ treeMap.set(parent.id, parent)
482
+ }
483
+
484
+ return treeMap
485
+ }
486
+
540
487
  remoteListOwner = async () => {
541
488
  const remoteUidTree = await owner.remoteListUsers(
542
489
  this.props.pid,
@@ -666,7 +613,7 @@ export default class Sftp extends Component {
666
613
  this.sftp = sftp
667
614
  const update = {
668
615
  remote,
669
- remoteFileTree: buildTree(remote),
616
+ remoteFileTree: this.buildTree(remote, typeMap.remote),
670
617
  inited: true,
671
618
  remoteLoading: false
672
619
  }
@@ -748,7 +695,7 @@ export default class Sftp extends Component {
748
695
  }
749
696
  const update = {
750
697
  remote,
751
- remoteFileTree: buildTree(remote)
698
+ remoteFileTree: this.buildTree(remote, typeMap.remote)
752
699
  }
753
700
  this.setState(update)
754
701
  }
@@ -781,7 +728,7 @@ export default class Sftp extends Component {
781
728
  const update = {
782
729
  local,
783
730
  inited: true,
784
- localFileTree: buildTree(local),
731
+ localFileTree: this.buildTree(local, typeMap.local),
785
732
  localLoading: false
786
733
  }
787
734
  if (!noPathInit) {
@@ -953,6 +900,32 @@ export default class Sftp extends Component {
953
900
  )
954
901
  }
955
902
 
903
+ renderParentItem = (type) => {
904
+ const currentPath = this.state[`${type}Path`]
905
+ const parentPath = resolve(currentPath, '..')
906
+ // Don't render parent item if we're at the root
907
+ if (parentPath === currentPath) {
908
+ return null
909
+ }
910
+
911
+ const { id } = this.props.tab
912
+ const uniqueId = `parent-${parentPath}-${id}-${type}`
913
+
914
+ return {
915
+ type,
916
+ isDirectory: true,
917
+ ...getFolderFromFilePath(parentPath, type === typeMap.remote),
918
+ id: uniqueId,
919
+ size: 0,
920
+ modifyTime: 0,
921
+ accessTime: 0,
922
+ mode: 0,
923
+ owner: '',
924
+ group: '',
925
+ isParent: true
926
+ }
927
+ }
928
+
956
929
  renderHistory = (type) => {
957
930
  const currentPath = this.state[type + 'Path']
958
931
  const options = this.state[type + 'PathHistory']
@@ -999,6 +972,7 @@ export default class Sftp extends Component {
999
972
  store: window.store,
1000
973
  id,
1001
974
  type,
975
+ parentItem: this.renderParentItem(type),
1002
976
  ...this.props,
1003
977
  ...pick(
1004
978
  this,
@@ -78,9 +78,11 @@
78
78
  left 10px
79
79
  top 10px
80
80
  font-size 50px
81
+ .file-props-div
82
+ display flex
83
+ width 100%
81
84
  .file-props
82
85
  margin-left 70px
83
-
84
86
  .opacity-loop
85
87
  opacity 0.3
86
88
  animation blinker 5s linear infinite
@@ -97,11 +99,9 @@
97
99
 
98
100
  //list table
99
101
  .sftp-file-prop
102
+ flex 0 0 auto
100
103
  height 32px
101
104
  line-height 30px
102
- position absolute
103
- left 0
104
- width 100px
105
105
  padding 0 5px
106
106
  background main
107
107
  overflow hidden
@@ -109,11 +109,8 @@
109
109
  text-overflow ellipsis
110
110
  pointer-events none
111
111
  cursor default
112
- z-index 4
113
112
  color text
114
- &:last-child
115
- right 0 !important
116
- width auto !important
113
+
117
114
  .file-bg
118
115
  position absolute
119
116
  left 0
@@ -125,26 +122,13 @@
125
122
  .sftp-header-item
126
123
  height 28px
127
124
  line-height 25px
128
- position absolute
129
- left 0
130
- width 100px
131
- border 1px solid main
132
- border-right none
133
125
  padding 0 5px
134
126
  background main
135
127
  overflow hidden
136
128
  white-space nowrap
137
129
  text-overflow ellipsis
138
130
  color text
139
- &:last-child
140
- border-right 1px solid main
141
- right 0 !important
142
- width auto !important
143
- .sftp-header-handle
144
- z-index 20
145
- cursor ew-resize
146
- border none
147
- background none
131
+
148
132
  .sftp-file-table-header
149
133
  height 30px
150
134
  .sftp-table
@@ -12,6 +12,7 @@ import {
12
12
  getFileExt,
13
13
  checkFolderSize
14
14
  } from './file-read'
15
+ import { refsStatic, refs } from '../common/ref'
15
16
  import generate from '../../common/uid'
16
17
  import resolve from '../../common/resolve'
17
18
  import deepCopy from 'json-deep-copy'
@@ -24,6 +25,8 @@ export default class TransferConflictStore extends PureComponent {
24
25
  }
25
26
 
26
27
  componentDidMount () {
28
+ this.id = 'transfer-conflict'
29
+ refsStatic.add(this.id, this)
27
30
  this.watchFile()
28
31
  }
29
32
 
@@ -40,7 +43,7 @@ export default class TransferConflictStore extends PureComponent {
40
43
  }
41
44
 
42
45
  remoteCheckExist = (path, sessionId) => {
43
- const sftp = window.sftps[sessionId]
46
+ const sftp = refs.get('sftp-' + sessionId).sftp
44
47
  return getRemoteFileInfo(sftp, path)
45
48
  .then(r => r)
46
49
  .catch(() => false)
@@ -131,23 +134,17 @@ export default class TransferConflictStore extends PureComponent {
131
134
  window.store.transferToConfirm = tr
132
135
  }
133
136
 
134
- onDecision = (event) => {
137
+ onDecision = (data) => {
135
138
  if (
136
- event &&
137
- event.data &&
138
- event.data.id === this.currentId
139
+ data.id === this.currentId
139
140
  ) {
140
141
  this.currentId = ''
141
- this.updateTransferAction(event.data)
142
+ this.updateTransferAction(data)
142
143
  this.onConfirm = false
143
144
  window.removeEventListener('message', this.onDecision)
144
145
  }
145
146
  }
146
147
 
147
- waitForSignal = () => {
148
- window.addEventListener('message', this.onDecision)
149
- }
150
-
151
148
  updateData = () => {
152
149
  const {
153
150
  store
@@ -239,7 +236,7 @@ export default class TransferConflictStore extends PureComponent {
239
236
  }
240
237
  if (fromFile.isDirectory && typeFrom !== typeTo) {
241
238
  const props = {
242
- sftp: window.sftps[sessionId]
239
+ sftp: refs.get('sftp-' + sessionId).sftp
243
240
  }
244
241
  const skip = await checkFolderSize(props, fromFile)
245
242
  .then(d => d && typeFrom !== typeTo)
@@ -260,7 +257,6 @@ export default class TransferConflictStore extends PureComponent {
260
257
  transfer: tr
261
258
  })
262
259
  } else if (toFile && !action && !skipConfirm) {
263
- this.waitForSignal(id)
264
260
  if (!this.onConfirm) {
265
261
  this.onConfirm = true
266
262
  assign(tr, {
@@ -2,7 +2,7 @@ import { Component } from 'react'
2
2
  import copy from 'json-deep-copy'
3
3
  import { isFunction } from 'lodash-es'
4
4
  import generate from '../../common/uid'
5
- import { typeMap, transferTypeMap, commonActions } from '../../common/constants'
5
+ import { typeMap, transferTypeMap } from '../../common/constants'
6
6
  import fs from '../../common/fs'
7
7
  import format, { computeLeftTime, computePassedTime } from './transfer-speed-format'
8
8
  import {
@@ -10,7 +10,7 @@ import {
10
10
  } from './file-read'
11
11
  import resolve from '../../common/resolve'
12
12
  import delay from '../../common/wait'
13
- import postMsg from '../../common/post-msg'
13
+ import { refs } from '../common/ref'
14
14
  import { zipCmd, unzipCmd, rmCmd, mvCmd, mkdirCmd } from './zip'
15
15
  import './transfer.styl'
16
16
 
@@ -71,19 +71,11 @@ export default class TransportAction extends Component {
71
71
  }
72
72
 
73
73
  remoteList = () => {
74
- postMsg({
75
- action: commonActions.sftpList,
76
- sessionId: this.sessionId,
77
- type: typeMap.remote
78
- })
74
+ window.store.remoteList(this.sessionId)
79
75
  }
80
76
 
81
77
  localList = () => {
82
- postMsg({
83
- action: commonActions.sftpList,
84
- sessionId: this.sessionId,
85
- type: typeMap.local
86
- })
78
+ window.store.localList(this.sessionId)
87
79
  }
88
80
 
89
81
  onEnd = (update = {}) => {
@@ -200,7 +192,7 @@ export default class TransportAction extends Component {
200
192
  this.onError(e)
201
193
  })
202
194
  }
203
- const sftp = window.sftps[sessionId]
195
+ const sftp = refs.get('sftp-' + sessionId).sftp
204
196
  return sftp[operation](fromPath, toPath)
205
197
  .then(this.onEnd)
206
198
  .catch(e => {
@@ -342,7 +334,7 @@ export default class TransportAction extends Component {
342
334
  ? fromPath
343
335
  : toPath
344
336
  const mode = toFile.mode || fromMode
345
- const sftp = window.sftps[this.sessionId]
337
+ const sftp = refs.get('sftp-' + this.sessionId).sftp
346
338
  this.transport = await sftp[transferType]({
347
339
  remotePath,
348
340
  localPath,
@@ -419,7 +411,7 @@ export default class TransportAction extends Component {
419
411
  return fs.mkdir(toPath)
420
412
  .catch(this.onError)
421
413
  }
422
- const sftp = window.sftps[sessionId]
414
+ const sftp = refs.get('sftp-' + sessionId).sftp
423
415
  return sftp.mkdir(toPath)
424
416
  .catch(this.onError)
425
417
  }
@@ -6,15 +6,84 @@
6
6
  import React from 'react'
7
7
  import { shortcutExtend } from './shortcut-handler.js'
8
8
  import { throttle } from 'lodash-es'
9
+ import {
10
+ typeMap
11
+ } from '../../common/constants'
12
+ import { refs, refsStatic } from '../common/ref'
13
+ import keyControlPressed from '../../common/key-control-pressed'
14
+ import keyPressed from '../../common/key-pressed'
9
15
 
10
16
  class ShortcutControl extends React.PureComponent {
11
17
  componentDidMount () {
12
18
  const onEvent = this.handleKeyboardEvent.bind(this)
13
- window.addEventListener('keydown', onEvent)
14
- window.addEventListener('mousedown', onEvent)
15
- window.addEventListener('mousewheel', onEvent)
19
+ document.addEventListener('keydown', this.onEvent, true)
20
+ document.addEventListener('mousedown', onEvent)
21
+ document.addEventListener('mousewheel', onEvent)
16
22
  }
17
23
 
24
+ onEvent = (e) => {
25
+ // First check SFTP shortcuts
26
+ this.handleSftpKeyboardEvent(e)
27
+ // Then handle extended shortcuts
28
+ this.handleKeyboardEvent(e)
29
+ }
30
+
31
+ getActiveSftp = () => {
32
+ const { activeTabId } = window.store
33
+ if (!activeTabId) return null
34
+ const ref = refs.get('sftp-' + activeTabId)
35
+ if (!ref || !ref.isActive()) return null
36
+ return ref
37
+ }
38
+
39
+ // SFTP shortcuts handler
40
+ handleSftpKeyboardEvent = (e) => {
41
+ const activeSftp = this.getActiveSftp()
42
+ if (!activeSftp || activeSftp.state.onDelete) {
43
+ return
44
+ }
45
+
46
+ const lastClickedFile = activeSftp.state.lastClickedFile || {
47
+ type: typeMap.local
48
+ }
49
+ const { type } = lastClickedFile
50
+ const { inputFocus } = activeSftp
51
+
52
+ if (keyControlPressed(e) && keyPressed(e, 'keyA') && !inputFocus) {
53
+ e.stopPropagation()
54
+ activeSftp.selectAll(type, e)
55
+ } else if (keyPressed(e, 'arrowdown') && !inputFocus) {
56
+ e.stopPropagation()
57
+ activeSftp.selectNext(type)
58
+ } else if (keyPressed(e, 'arrowup') && !inputFocus) {
59
+ e.stopPropagation()
60
+ activeSftp.selectPrev(type)
61
+ } else if (keyPressed(e, 'delete') && !inputFocus && !activeSftp.state.onEditFile) {
62
+ e.stopPropagation()
63
+ activeSftp.delFiles(type)
64
+ } else if (keyPressed(e, 'enter') && !inputFocus && !activeSftp.onDelete) {
65
+ e.stopPropagation()
66
+ activeSftp.enter(type, e)
67
+ } else if (keyControlPressed(e) && keyPressed(e, 'keyC') && !inputFocus) {
68
+ e.stopPropagation()
69
+ activeSftp.doCopy(type, e)
70
+ } else if (keyControlPressed(e) && keyPressed(e, 'keyX') && !inputFocus) {
71
+ e.stopPropagation()
72
+ activeSftp.doCut(type, e)
73
+ } else if (keyControlPressed(e) && keyPressed(e, 'keyV') && !inputFocus) {
74
+ e.stopPropagation()
75
+ activeSftp.doPaste(type, e)
76
+ } else if (keyPressed(e, 'f5')) {
77
+ e.stopPropagation()
78
+ activeSftp.onGoto(type)
79
+ }
80
+ }
81
+
82
+ searchShortcut = throttle((e) => {
83
+ e.stopPropagation()
84
+ refsStatic.get('term-search')?.toggleSearch()
85
+ }, 500)
86
+
18
87
  closeCurrentTabShortcut = throttle((e) => {
19
88
  e.stopPropagation()
20
89
  const { activeTabId } = window.store
@@ -68,7 +68,6 @@ export function shortcutExtend (Cls) {
68
68
  !altKey &&
69
69
  !ctrlKey
70
70
  ) {
71
- console.log('handleKeyboardEvent: Handling Backspace key')
72
71
  this.props.onDelKeyPressed()
73
72
  const delKey = this.props.config.backspaceMode === '^?' ? 8 : 127
74
73
  const altDelDelKey = delKey === 8 ? 127 : 8
@@ -1,4 +1,4 @@
1
- import React, { memo } from 'react'
1
+ import React, { memo, useRef } from 'react'
2
2
  import DragHandle from '../common/drag-handle'
3
3
  import './right-side-panel.styl'
4
4
  import {
@@ -22,6 +22,8 @@ export default memo(function RightSidePanel (
22
22
  rightPanelTab
23
23
  }
24
24
  ) {
25
+ const panelRef = useRef(null)
26
+
25
27
  if (!rightPanelVisible) {
26
28
  return null
27
29
  }
@@ -34,8 +36,9 @@ export default memo(function RightSidePanel (
34
36
  }
35
37
 
36
38
  function onDragMove (nw) {
37
- const el = document.getElementById('right-side-panel')
38
- el.style.width = nw + 'px'
39
+ if (panelRef.current) {
40
+ panelRef.current.style.width = nw + 'px'
41
+ }
39
42
  }
40
43
 
41
44
  function onClose () {
@@ -48,7 +51,7 @@ export default memo(function RightSidePanel (
48
51
 
49
52
  const panelProps = {
50
53
  className: 'right-side-panel animate-fast' + (rightPanelPinned ? ' right-side-panel-pinned' : ''),
51
- id: 'right-side-panel',
54
+ ref: panelRef,
52
55
  style: {
53
56
  width: `${rightPanelWidth}px`
54
57
  }