@electerm/electerm-react 3.7.18 → 3.8.8

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.
@@ -155,7 +155,7 @@ export default class TransportAction extends Component {
155
155
  const finishTime = Date.now()
156
156
  if (!config.disableTransferHistory) {
157
157
  const fromFile = transfer.fromFile || this.fromFile
158
- const size = update.size || fromFile.size
158
+ const size = update.size ?? update.transferred ?? fromFile.size
159
159
  const r = copy(transfer)
160
160
  assign(r, {
161
161
  finishTime,
@@ -182,20 +182,27 @@ export default class TransportAction extends Component {
182
182
  return
183
183
  }
184
184
  const { transfer } = this.props
185
+ const fromFile = transfer.fromFile || this.fromFile || {}
186
+ const transferredValue = typeof transferred === 'object' && transferred !== null
187
+ ? transferred.transferred
188
+ : transferred
189
+ const total = typeof transferred === 'object' && transferred !== null
190
+ ? (transferred.total || fromFile.size || 0)
191
+ : (fromFile.size || 0)
185
192
  const up = {}
186
- const total = transfer.fromFile.size
187
193
  let percent = total === 0
188
194
  ? 100
189
- : Math.floor(100 * transferred / total)
195
+ : Math.floor(100 * transferredValue / total)
190
196
  percent = percent >= 100 ? 100 : percent
197
+ this.total = total
191
198
  up.percent = percent
192
199
  up.status = 'active'
193
- up.transferred = transferred
200
+ up.transferred = transferredValue
194
201
  up.startTime = this.startTime
195
- up.speed = format(transferred, up.startTime)
202
+ up.speed = format(transferredValue, up.startTime)
196
203
  assign(
197
204
  up,
198
- computeLeftTime(transferred, total, up.startTime)
205
+ computeLeftTime(transferredValue, total, up.startTime)
199
206
  )
200
207
  up.passedTime = computePassedTime(up.startTime)
201
208
  this.update(up)
@@ -290,6 +297,7 @@ export default class TransportAction extends Component {
290
297
  this.transport = await sftp[transferType]({
291
298
  remotePath,
292
299
  localPath,
300
+ isDirectory: !!fromFile.isDirectory,
293
301
  options: { mode },
294
302
  onData: this.onData,
295
303
  onError: this.onError,
@@ -528,6 +536,9 @@ export default class TransportAction extends Component {
528
536
  }
529
537
  if (zip) {
530
538
  return this.zipTransferFolder()
539
+ }
540
+ if (!this.isFtp) {
541
+ return this.transferFile()
531
542
  } else {
532
543
  await this.transferFolderRecursive()
533
544
  }
@@ -63,6 +63,7 @@ export default auto(function SettingModalWrap (props) {
63
63
  ...props0,
64
64
  bookmarkSelectMode,
65
65
  bookmarkGroups,
66
+ bookmarkGroupTree: store.bookmarkGroupTree,
66
67
  bookmarksMap: store.bookmarksMap,
67
68
  bookmarks,
68
69
  ...pick(store, [
@@ -46,6 +46,11 @@ export default class FileMode extends React.PureComponent {
46
46
  }
47
47
 
48
48
  showFileInfoModal (data) {
49
+ if (data.pid !== this.remoteExecPid) {
50
+ this.remoteExecPid = data.pid
51
+ this.remoteExecPlatform = null
52
+ this.remoteExecPlatformPromise = null
53
+ }
49
54
  this.setStateProxy({
50
55
  ...data,
51
56
  size: 0,
@@ -115,6 +120,47 @@ export default class FileMode extends React.PureComponent {
115
120
  }
116
121
  }
117
122
 
123
+ escapePowerShellPath = (value) => {
124
+ return String(value).replace(/'/g, "''")
125
+ }
126
+
127
+ normalizeRemoteWindowsPath = (value) => {
128
+ return String(value).replace(/^\/([a-zA-Z]:)/, '$1')
129
+ }
130
+
131
+ getRemoteExecPlatform = async () => {
132
+ if (this.remoteExecPid === this.state.pid && this.remoteExecPlatform) {
133
+ return this.remoteExecPlatform
134
+ }
135
+ if (this.remoteExecPid !== this.state.pid) {
136
+ this.remoteExecPid = this.state.pid
137
+ this.remoteExecPlatform = null
138
+ this.remoteExecPlatformPromise = null
139
+ }
140
+ if (!this.remoteExecPlatformPromise) {
141
+ this.remoteExecPlatformPromise = runCmd(this.state.pid, 'cmd.exe /d /s /c ver')
142
+ .then(output => {
143
+ return String(output).toLowerCase().includes('windows')
144
+ ? 'windows'
145
+ : 'posix'
146
+ })
147
+ .catch(() => 'posix')
148
+ .then(platform => {
149
+ this.remoteExecPlatform = platform
150
+ return platform
151
+ })
152
+ }
153
+ return this.remoteExecPlatformPromise
154
+ }
155
+
156
+ calcRemoteWin = async (folder) => {
157
+ const winFolder = this.normalizeRemoteWindowsPath(folder)
158
+ const escapedFolder = this.escapePowerShellPath(winFolder)
159
+ const cmd = `powershell.exe -NoLogo -NonInteractive -NoProfile -Command "$result = Get-ChildItem -LiteralPath '${escapedFolder}' -Recurse -File | Measure-Object -Property Length -Sum; if ($null -eq $result.Sum) { 0 } else { [int64]$result.Sum }"`
160
+ const result = await runCmd(this.state.pid, cmd).catch(window.store.onError)
161
+ return filesize(parseInt(String(result || '').trim(), 10) || 0)
162
+ }
163
+
118
164
  calcLocal = async (folder) => {
119
165
  const cmd = isWin
120
166
  ? `Get-ChildItem -Recurse '${folder}' | Measure-Object -Property Length -Sum`
@@ -125,6 +171,10 @@ export default class FileMode extends React.PureComponent {
125
171
  }
126
172
 
127
173
  calcRemote = async (folder) => {
174
+ const platform = await this.getRemoteExecPlatform()
175
+ if (platform === 'windows') {
176
+ return this.calcRemoteWin(folder)
177
+ }
128
178
  const cmd = `du -sh '${folder}'`
129
179
  const r = await runCmd(
130
180
  this.state.pid,
@@ -1037,13 +1037,13 @@ export default class FileSection extends React.Component {
1037
1037
  icon: iconType,
1038
1038
  text: transferText
1039
1039
  })
1040
- if (isDirectory && !this.props.isFtp) {
1041
- res.push({
1042
- func: 'zipAndTransfer',
1043
- icon: 'FileZipOutlined',
1044
- text: e('compressAndTransfer')
1045
- })
1046
- }
1040
+ // if (isDirectory && !this.props.isFtp) {
1041
+ // res.push({
1042
+ // func: 'zipAndTransfer',
1043
+ // icon: 'FileZipOutlined',
1044
+ // text: e('compressAndTransfer')
1045
+ // })
1046
+ // }
1047
1047
  }
1048
1048
  if (!isDirectory && isRealFile && isLocal) {
1049
1049
  res.push({
@@ -5,41 +5,30 @@ import {
5
5
  CaretRightOutlined
6
6
  } from '@ant-design/icons'
7
7
 
8
- function hasChildren (group) {
9
- return Boolean(
10
- group?.bookmarkIds?.length ||
11
- group?.bookmarkGroupIds?.length
12
- )
13
- }
14
-
15
- function isOpen (props) {
16
- return Boolean(props.keyword) || props.expandedKeys.includes(props.group.id)
17
- }
18
-
19
8
  function areEqual (prevProps, nextProps) {
20
9
  return prevProps.group?.id === nextProps.group?.id &&
21
- hasChildren(prevProps.group) === hasChildren(nextProps.group) &&
22
- Boolean(prevProps.keyword) === Boolean(nextProps.keyword) &&
23
- isOpen(prevProps) === isOpen(nextProps)
10
+ prevProps.hasChildren === nextProps.hasChildren &&
11
+ prevProps.shouldOpen === nextProps.shouldOpen
24
12
  }
25
13
 
26
14
  function TreeExpander (props) {
15
+ const { group } = props
16
+
27
17
  function onExpand (e) {
28
18
  e.stopPropagation()
29
19
  props.onExpand(group)
30
20
  }
21
+
31
22
  function onUnExpand (e) {
32
23
  e.stopPropagation()
33
24
  props.onUnExpand(group)
34
25
  }
35
- const { group } = props
36
- if (
37
- !group?.bookmarkIds?.length &&
38
- !group?.bookmarkGroupIds?.length
39
- ) {
26
+
27
+ if (!props.hasChildren) {
40
28
  return null
41
29
  }
42
- const shouldOpen = props.keyword || props.expandedKeys.includes(group.id)
30
+
31
+ const shouldOpen = props.shouldOpen
43
32
  const Icon = shouldOpen
44
33
  ? CaretDownOutlined
45
34
  : CaretRightOutlined
@@ -0,0 +1,156 @@
1
+ import {
2
+ CloseOutlined,
3
+ CopyOutlined,
4
+ EditOutlined,
5
+ FolderAddOutlined,
6
+ FolderOpenOutlined,
7
+ RightSquareOutlined
8
+ } from '@ant-design/icons'
9
+ import {
10
+ Popconfirm,
11
+ Tooltip
12
+ } from 'antd'
13
+ import { useState } from 'react'
14
+ import {
15
+ defaultBookmarkGroupId
16
+ } from '../../common/constants'
17
+
18
+ const e = window.translate
19
+
20
+ export default function TreeItemOp (props) {
21
+ const [pendingDeleteItem, setPendingDeleteItem] = useState(null)
22
+ const {
23
+ item,
24
+ isGroup,
25
+ staticList,
26
+ del,
27
+ openAll,
28
+ openMoveModal,
29
+ editItem,
30
+ addSubCat,
31
+ duplicateItem
32
+ } = props
33
+
34
+ if (!item) {
35
+ return null
36
+ }
37
+
38
+ const isDefaultGroup = item.id === defaultBookmarkGroupId
39
+ const canShowSharedOps = !staticList && !isDefaultGroup
40
+
41
+ const handleDel = (event) => {
42
+ del(pendingDeleteItem || item, event)
43
+ setPendingDeleteItem(null)
44
+ }
45
+
46
+ const handleDeleteOpenChange = (open) => {
47
+ setPendingDeleteItem(open ? item : null)
48
+ }
49
+
50
+ const handleOpenMoveModal = (event) => {
51
+ openMoveModal(event, item, isGroup)
52
+ }
53
+
54
+ const handleEditItem = (event) => {
55
+ editItem(event, item, isGroup)
56
+ }
57
+
58
+ const handleAddSubCat = (event) => {
59
+ addSubCat(event, item)
60
+ }
61
+
62
+ const handleDuplicateItem = (event) => {
63
+ duplicateItem(event, item)
64
+ }
65
+
66
+ const handleOpenAll = () => {
67
+ openAll(item)
68
+ }
69
+
70
+ const buttons = []
71
+
72
+ if (isGroup && !staticList) {
73
+ buttons.push(
74
+ <FolderAddOutlined
75
+ key='new-tree'
76
+ title={`${e('new')} ${e('bookmarkCategory')}`}
77
+ onClick={handleAddSubCat}
78
+ className='pointer tree-control-btn'
79
+ />
80
+ )
81
+ }
82
+
83
+ if (isGroup && staticList) {
84
+ buttons.push(
85
+ <Tooltip title={e('openAll')} key='open-all-tooltip'>
86
+ <FolderOpenOutlined
87
+ key='open-all-tree'
88
+ onClick={handleOpenAll}
89
+ className='pointer open-all-icon tree-control-btn'
90
+ />
91
+ </Tooltip>
92
+ )
93
+ }
94
+
95
+ if (!isGroup && !staticList && item.id) {
96
+ buttons.push(
97
+ <CopyOutlined
98
+ key='duplicate-tree'
99
+ title={e('duplicate')}
100
+ className='pointer tree-control-btn'
101
+ onClick={handleDuplicateItem}
102
+ />
103
+ )
104
+ }
105
+
106
+ if (canShowSharedOps) {
107
+ buttons.push(
108
+ <RightSquareOutlined
109
+ key='move-tree'
110
+ className='pointer tree-control-btn'
111
+ onClick={handleOpenMoveModal}
112
+ />
113
+ )
114
+ }
115
+
116
+ if (!isDefaultGroup && !staticList) {
117
+ buttons.push(
118
+ <Popconfirm
119
+ key='delete-tree'
120
+ title={e('del') + '?'}
121
+ onConfirm={handleDel}
122
+ onCancel={() => setPendingDeleteItem(null)}
123
+ onOpenChange={handleDeleteOpenChange}
124
+ okText={e('del')}
125
+ cancelText={e('cancel')}
126
+ placement='top'
127
+ >
128
+ <CloseOutlined title={e('del')} className='pointer tree-control-btn' />
129
+ </Popconfirm>
130
+ )
131
+ }
132
+
133
+ const shouldShowEdit = !((staticList && isGroup) || (!staticList && !isGroup))
134
+ if (shouldShowEdit) {
135
+ buttons.push(
136
+ <EditOutlined
137
+ title={e('edit')}
138
+ key='edit-tree'
139
+ onClick={handleEditItem}
140
+ className='pointer edit-icon tree-control-btn'
141
+ />
142
+ )
143
+ }
144
+
145
+ if (!buttons.length) {
146
+ return null
147
+ }
148
+
149
+ return (
150
+ <div
151
+ className={`tree-item-op-wrap${pendingDeleteItem ? ' is-active' : ''}`}
152
+ >
153
+ {buttons}
154
+ </div>
155
+ )
156
+ }
@@ -0,0 +1,47 @@
1
+ import {
2
+ CheckOutlined,
3
+ CloseOutlined
4
+ } from '@ant-design/icons'
5
+ import InputAutoFocus from '../common/input-auto-focus'
6
+ import { CategoryColorPicker } from './category-color-picker.jsx'
7
+ import { getRandomDefaultColor } from '../../common/rand-hex-color.js'
8
+ import { treeEditorRowHeight } from './tree-list-layout'
9
+
10
+ export default function TreeListEditorOverlay ({ editor }) {
11
+ if (!editor) {
12
+ return null
13
+ }
14
+
15
+ const confirm = (
16
+ <span>
17
+ <CheckOutlined className='pointer' onClick={editor.handleSubmit} />
18
+ <CloseOutlined className='mg1l pointer' onClick={editor.handleCancel} />
19
+ </span>
20
+ )
21
+ const colorPicker = (
22
+ <CategoryColorPicker
23
+ value={editor.color || getRandomDefaultColor()}
24
+ onChange={editor.handleColorChange}
25
+ />
26
+ )
27
+
28
+ return (
29
+ <div
30
+ className='tree-list-editor-overlay'
31
+ style={{
32
+ top: editor.top,
33
+ left: editor.left,
34
+ height: treeEditorRowHeight
35
+ }}
36
+ >
37
+ <InputAutoFocus
38
+ value={editor.title}
39
+ onChange={editor.handleTitleChange}
40
+ onPressEnter={editor.handleSubmit}
41
+ prefix={colorPicker}
42
+ suffix={confirm}
43
+ selectall={editor.selectall}
44
+ />
45
+ </div>
46
+ )
47
+ }
@@ -2,30 +2,12 @@
2
2
  * tree list for bookmarks
3
3
  */
4
4
 
5
- import { memo, useState } from 'react'
6
-
7
- import {
8
- CloseOutlined,
9
- CopyOutlined,
10
- EditOutlined,
11
- FolderAddOutlined,
12
- FolderOpenOutlined,
13
- RightSquareOutlined
14
- } from '@ant-design/icons'
15
- import {
16
- Popconfirm,
17
- Tooltip
18
- } from 'antd'
5
+ import { memo } from 'react'
19
6
  import createName, { createTitleTag } from '../../common/create-title'
20
7
  import classnames from 'classnames'
21
- import {
22
- defaultBookmarkGroupId
23
- } from '../../common/constants'
24
8
  import highlight from '../common/highlight'
25
9
  import uid from '../../common/uid'
26
10
 
27
- const e = window.translate
28
-
29
11
  function getItemLabel (item, isGroup) {
30
12
  return isGroup
31
13
  ? item?.title || ''
@@ -35,12 +17,15 @@ function getItemLabel (item, isGroup) {
35
17
  function areEqual (prevProps, nextProps) {
36
18
  const prevSelected = prevProps.selectedItemId === prevProps.item.id
37
19
  const nextSelected = nextProps.selectedItemId === nextProps.item.id
20
+ const prevSearchSelected = Boolean(prevProps.searchSelected)
21
+ const nextSearchSelected = Boolean(nextProps.searchSelected)
38
22
 
39
23
  return prevProps.isGroup === nextProps.isGroup &&
40
24
  prevProps.parentId === nextProps.parentId &&
41
25
  prevProps.staticList === nextProps.staticList &&
42
26
  prevProps.keyword === nextProps.keyword &&
43
27
  prevSelected === nextSelected &&
28
+ prevSearchSelected === nextSearchSelected &&
44
29
  prevProps.item.id === nextProps.item.id &&
45
30
  prevProps.item.level === nextProps.item.level &&
46
31
  prevProps.item.color === nextProps.item.color &&
@@ -49,145 +34,10 @@ function areEqual (prevProps, nextProps) {
49
34
  }
50
35
 
51
36
  function TreeListItem (props) {
52
- const [hovered, setHovered] = useState(false)
53
-
54
- const handleDel = (e) => {
55
- props.del(props.item, e)
56
- }
57
-
58
- const renderDelBtn = (item) => {
59
- if (props.item.id === defaultBookmarkGroupId || props.staticList) {
60
- return null
61
- }
62
- return (
63
- <Popconfirm
64
- title={e('del') + '?'}
65
- onConfirm={handleDel}
66
- okText={e('del')}
67
- cancelText={e('cancel')}
68
- placement='top'
69
- >
70
- <CloseOutlined title={e('del')} className='pointer tree-control-btn' />
71
- </Popconfirm>
72
- )
73
- }
74
-
75
- const renderOperationBtn = (item, isGroup) => {
76
- if (props.staticList || props.item.id === defaultBookmarkGroupId) {
77
- return null
78
- }
79
- return (
80
- <RightSquareOutlined
81
- className='pointer tree-control-btn'
82
- onClick={openMoveModal}
83
- />
84
- )
85
- }
86
-
87
- const handleOpenAll = () => {
88
- props.openAll(props.item)
89
- }
90
-
91
- const openMoveModal = (e) => {
92
- props.openMoveModal(e, props.item, props.isGroup)
93
- }
94
-
95
- const handleEditItem = (e) => {
96
- props.editItem(e, props.item, props.isGroup)
97
- }
98
-
99
- const handleAddSubCat = (e) => {
100
- props.addSubCat(e, props.item)
101
- }
102
-
103
- const renderAddNewSubGroupBtn = () => {
104
- if (props.staticList) {
105
- return null
106
- }
107
- return (
108
- <FolderAddOutlined
109
- key='new-tree'
110
- title={`${e('new')} ${e('bookmarkCategory')}`}
111
- onClick={handleAddSubCat}
112
- className='pointer tree-control-btn'
113
- />
114
- )
115
- }
116
-
117
- const renderEditBtn = () => {
118
- const {
119
- isGroup, staticList
120
- } = props
121
- if (
122
- (staticList && isGroup) ||
123
- (!staticList && !isGroup)
124
- ) {
125
- return null
126
- }
127
- return (
128
- <EditOutlined
129
- title={e('edit')}
130
- key='edit-tree'
131
- onClick={handleEditItem}
132
- className='pointer edit-icon tree-control-btn'
133
- />
134
- )
135
- }
136
-
137
37
  const onSelect = (e) => {
138
38
  props.onSelect(e)
139
39
  }
140
40
 
141
- const renderOpenAll = () => {
142
- const {
143
- staticList,
144
- isGroup
145
- } = props
146
- if (
147
- (staticList && !isGroup) ||
148
- !staticList
149
- ) {
150
- return null
151
- }
152
- return (
153
- <Tooltip title={e('openAll')}>
154
- <FolderOpenOutlined
155
- key='open-all-tree'
156
- onClick={handleOpenAll}
157
- className='pointer open-all-icon tree-control-btn'
158
- />
159
- </Tooltip>
160
- )
161
- }
162
-
163
- const handleDuplicateItem = (e) => {
164
- props.duplicateItem(e, props.item)
165
- }
166
-
167
- const renderDuplicateBtn = () => {
168
- const {
169
- item,
170
- staticList
171
- } = props
172
- if (!item.id || staticList) {
173
- return null
174
- }
175
- return (
176
- <CopyOutlined
177
- title={e('duplicate')}
178
- className='pointer tree-control-btn'
179
- onClick={handleDuplicateItem}
180
- />
181
- )
182
- }
183
-
184
- const renderGroupBtns = () => {
185
- return [
186
- renderAddNewSubGroupBtn(),
187
- renderOpenAll()
188
- ]
189
- }
190
-
191
41
  const onDragOver = e => {
192
42
  props.onDragOver(e)
193
43
  }
@@ -215,7 +65,8 @@ function TreeListItem (props) {
215
65
  } = props
216
66
  const cls = classnames(
217
67
  {
218
- selected: selectedItemId === item.id
68
+ selected: selectedItemId === item.id,
69
+ 'search-selected': props.searchSelected
219
70
  },
220
71
  'tree-item',
221
72
  {
@@ -251,8 +102,6 @@ function TreeListItem (props) {
251
102
  'data-item-id': item.id,
252
103
  'data-parent-id': props.parentId,
253
104
  'data-is-group': isGroup ? 'true' : 'false',
254
- onMouseEnter: () => setHovered(true),
255
- onMouseLeave: () => setHovered(false),
256
105
  onDragOver,
257
106
  onDragStart,
258
107
  onDragEnter,
@@ -277,19 +126,6 @@ function TreeListItem (props) {
277
126
  >
278
127
  {colorTag}{tag}{titleHighlight}
279
128
  </div>
280
- {
281
- hovered && isGroup
282
- ? renderGroupBtns()
283
- : null
284
- }
285
- {
286
- hovered && !isGroup
287
- ? renderDuplicateBtn()
288
- : null
289
- }
290
- {hovered ? renderOperationBtn() : null}
291
- {hovered ? renderDelBtn() : null}
292
- {hovered ? renderEditBtn() : null}
293
129
  </div>
294
130
  )
295
131
  }
@@ -0,0 +1,3 @@
1
+ export const treeLevelIndent = 12
2
+ export const treeRowHeight = 26
3
+ export const treeEditorRowHeight = 32