@electerm/electerm-react 1.38.65 → 1.38.70
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.
- package/client/common/constants.js +3 -2
- package/client/common/create-title.jsx +9 -1
- package/client/common/sftp.js +3 -0
- package/client/components/batch-op/batch-op.jsx +1 -6
- package/client/components/bookmark-form/bookmark-form.styl +3 -1
- package/client/components/bookmark-form/render-ssh-tunnel.jsx +210 -88
- package/client/components/bookmark-form/ssh-form-ui.jsx +1 -1
- package/client/components/main/main.jsx +14 -0
- package/client/components/sftp/{confirm-modal.jsx → confirm-modal-store.jsx} +81 -50
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sftp/sftp-entry.jsx +27 -37
- package/client/components/sftp/transfer-conflict-store.jsx +291 -0
- package/client/components/sftp/transport-action-store.jsx +430 -0
- package/client/components/sftp/transports-action-store.jsx +102 -0
- package/client/components/sftp/transports-ui-store.jsx +30 -0
- package/client/components/sidebar/transfer-list-control.jsx +5 -14
- package/client/components/sidebar/transport-ui.jsx +2 -12
- package/client/components/tabs/tab.jsx +43 -2
- package/client/components/tabs/tabs.styl +1 -1
- package/client/components/terminal/index.jsx +1 -0
- package/client/components/terminal/terminal-interactive.jsx +15 -0
- package/client/components/terminal-info/disk.jsx +9 -0
- package/client/store/index.js +4 -0
- package/client/store/init-state.js +2 -3
- package/client/store/sync.js +5 -2
- package/client/store/tab.js +1 -1
- package/client/store/transfer-list.js +55 -2
- package/client/store/watch.js +0 -8
- package/package.json +1 -1
- package/client/components/sftp/transfer-conflict.jsx +0 -323
- package/client/components/sftp/transport-action.jsx +0 -412
- package/client/components/sftp/transport-entry.jsx +0 -108
- package/client/components/sftp/transport-types.js +0 -8
- package/client/components/sftp/transports-action.jsx +0 -111
- package/client/components/sftp/transports-ui.jsx +0 -93
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { Component } from '../common/react-subx'
|
|
2
|
+
import copy from 'json-deep-copy'
|
|
3
|
+
import { findIndex, isFunction } from 'lodash-es'
|
|
4
|
+
import generate from '../../common/uid'
|
|
5
|
+
import { typeMap, transferTypeMap, commonActions } from '../../common/constants'
|
|
6
|
+
import fs from '../../common/fs'
|
|
7
|
+
import format, { computeLeftTime, computePassedTime } from './transfer-speed-format'
|
|
8
|
+
import { getFolderFromFilePath } from './file-read'
|
|
9
|
+
import resolve from '../../common/resolve'
|
|
10
|
+
import delay from '../../common/wait'
|
|
11
|
+
import postMsg from '../../common/post-msg'
|
|
12
|
+
import { zipCmd, unzipCmd, rmCmd, mvCmd, mkdirCmd } from './zip'
|
|
13
|
+
import './transfer.styl'
|
|
14
|
+
|
|
15
|
+
export default class TransportAction extends Component {
|
|
16
|
+
constructor (props) {
|
|
17
|
+
super(props)
|
|
18
|
+
this.sessionId = props.transfer.sessionId
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
inst = {}
|
|
22
|
+
unzipping = false
|
|
23
|
+
|
|
24
|
+
componentDidMount () {
|
|
25
|
+
if (this.props.inited) {
|
|
26
|
+
this.initTransfer()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
componentDidUpdate (prevProps) {
|
|
31
|
+
if (
|
|
32
|
+
prevProps.inited !== this.props.inited &&
|
|
33
|
+
this.props.inited === true
|
|
34
|
+
) {
|
|
35
|
+
this.initTransfer()
|
|
36
|
+
}
|
|
37
|
+
if (
|
|
38
|
+
this.props.cancel === true &&
|
|
39
|
+
prevProps.cancel !== true
|
|
40
|
+
) {
|
|
41
|
+
this.cancel()
|
|
42
|
+
}
|
|
43
|
+
if (
|
|
44
|
+
this.props.pause !== prevProps.pause
|
|
45
|
+
) {
|
|
46
|
+
if (this.props.pause) {
|
|
47
|
+
this.pause()
|
|
48
|
+
} else {
|
|
49
|
+
this.resume()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
update = (up) => {
|
|
55
|
+
const { store, transfer } = this.props
|
|
56
|
+
const {
|
|
57
|
+
fileTransfers
|
|
58
|
+
} = store
|
|
59
|
+
const index = findIndex(fileTransfers, t => t.id === transfer.id)
|
|
60
|
+
if (index < 0) {
|
|
61
|
+
return store.setFileTransfers(fileTransfers)
|
|
62
|
+
}
|
|
63
|
+
window.store.editTransfer(
|
|
64
|
+
fileTransfers[index].id,
|
|
65
|
+
up
|
|
66
|
+
)
|
|
67
|
+
Object.assign(fileTransfers[index], up)
|
|
68
|
+
store.setFileTransfers(fileTransfers)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
insert = (insts) => {
|
|
72
|
+
const { store, transfer } = this.props
|
|
73
|
+
const {
|
|
74
|
+
fileTransfers
|
|
75
|
+
} = store
|
|
76
|
+
const index = findIndex(fileTransfers, t => t.id === transfer.id)
|
|
77
|
+
fileTransfers.splice(index, 1, ...insts)
|
|
78
|
+
store.setFileTransfers(fileTransfers)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
remoteList = () => {
|
|
82
|
+
postMsg({
|
|
83
|
+
action: commonActions.sftpList,
|
|
84
|
+
sessionId: this.sessionId,
|
|
85
|
+
type: typeMap.remote
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
localList = () => {
|
|
90
|
+
postMsg({
|
|
91
|
+
action: commonActions.sftpList,
|
|
92
|
+
sessionId: this.sessionId,
|
|
93
|
+
type: typeMap.local
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
onEnd = (update = {}) => {
|
|
98
|
+
if (this.inst.onCancel) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
const {
|
|
102
|
+
transfer
|
|
103
|
+
} = this.props
|
|
104
|
+
const {
|
|
105
|
+
typeTo,
|
|
106
|
+
next
|
|
107
|
+
} = transfer
|
|
108
|
+
const cb = this[typeTo + 'List']
|
|
109
|
+
const finishTime = Date.now()
|
|
110
|
+
if (!this.props.store.config.disableTransferHistory) {
|
|
111
|
+
window.store.addTransferHistory(
|
|
112
|
+
{
|
|
113
|
+
...transfer,
|
|
114
|
+
...update,
|
|
115
|
+
finishTime,
|
|
116
|
+
startTime: this.inst.startTime,
|
|
117
|
+
size: transfer.fromFile.size,
|
|
118
|
+
next: null,
|
|
119
|
+
speed: format(transfer.fromFile.size, this.inst.startTime)
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
if (next) {
|
|
124
|
+
this.insert([copy(next)])
|
|
125
|
+
}
|
|
126
|
+
this.cancel(cb)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
onData = (transferred) => {
|
|
130
|
+
if (this.inst.onCancel) {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
const { transfer } = this.props
|
|
134
|
+
const up = {}
|
|
135
|
+
const total = transfer.fromFile.size
|
|
136
|
+
let percent = total === 0
|
|
137
|
+
? 0
|
|
138
|
+
: Math.floor(100 * transferred / total)
|
|
139
|
+
percent = percent >= 100 ? 99 : percent
|
|
140
|
+
up.percent = percent
|
|
141
|
+
up.status = 'active'
|
|
142
|
+
up.transferred = transferred
|
|
143
|
+
up.startTime = this.inst.startTime
|
|
144
|
+
up.speed = format(transferred, up.startTime)
|
|
145
|
+
Object.assign(
|
|
146
|
+
up,
|
|
147
|
+
computeLeftTime(transferred, total, up.startTime)
|
|
148
|
+
)
|
|
149
|
+
up.passedTime = computePassedTime(up.startTime)
|
|
150
|
+
this.update(up)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
cancel = (callback) => {
|
|
154
|
+
if (this.inst.onCancel) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
const {
|
|
158
|
+
transfer,
|
|
159
|
+
store
|
|
160
|
+
} = this.props
|
|
161
|
+
this.inst.onCancel = true
|
|
162
|
+
const { id } = transfer
|
|
163
|
+
this.inst.transport && this.inst.transport.destroy()
|
|
164
|
+
let {
|
|
165
|
+
fileTransfers
|
|
166
|
+
} = store
|
|
167
|
+
fileTransfers = fileTransfers.filter(t => {
|
|
168
|
+
return t.id !== id
|
|
169
|
+
})
|
|
170
|
+
store.setFileTransfers(fileTransfers)
|
|
171
|
+
if (isFunction(callback)) {
|
|
172
|
+
callback()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
pause = () => {
|
|
177
|
+
this.inst.transport && this.inst.transport.pause()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
resume = () => {
|
|
181
|
+
this.inst.transport && this.inst.transport.resume()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
mvOrCp = () => {
|
|
185
|
+
const {
|
|
186
|
+
transfer
|
|
187
|
+
} = this.props
|
|
188
|
+
const {
|
|
189
|
+
fromPath,
|
|
190
|
+
toPath,
|
|
191
|
+
typeFrom,
|
|
192
|
+
sessionId,
|
|
193
|
+
operation // 'mv' or 'cp'
|
|
194
|
+
} = transfer
|
|
195
|
+
if (typeFrom === typeMap.local) {
|
|
196
|
+
return fs[operation](fromPath, toPath)
|
|
197
|
+
.then(this.onEnd)
|
|
198
|
+
.catch(e => {
|
|
199
|
+
this.onEnd()
|
|
200
|
+
this.onError(e)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
const sftp = window.sftps[sessionId]
|
|
204
|
+
return sftp[operation](fromPath, toPath)
|
|
205
|
+
.then(this.onEnd)
|
|
206
|
+
.catch(e => {
|
|
207
|
+
this.onEnd()
|
|
208
|
+
this.onError(e)
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
zipTransfer = async () => {
|
|
213
|
+
const {
|
|
214
|
+
transfer
|
|
215
|
+
} = this.props
|
|
216
|
+
const {
|
|
217
|
+
fromPath,
|
|
218
|
+
toPath,
|
|
219
|
+
typeFrom,
|
|
220
|
+
sessionId
|
|
221
|
+
} = transfer
|
|
222
|
+
let p
|
|
223
|
+
let isFromRemote
|
|
224
|
+
if (typeFrom === typeMap.local) {
|
|
225
|
+
isFromRemote = false
|
|
226
|
+
p = await fs.zipFolder(fromPath)
|
|
227
|
+
} else {
|
|
228
|
+
isFromRemote = true
|
|
229
|
+
p = await zipCmd('', sessionId, fromPath)
|
|
230
|
+
}
|
|
231
|
+
const { name } = getFolderFromFilePath(p, isFromRemote)
|
|
232
|
+
const { path } = getFolderFromFilePath(toPath, !isFromRemote)
|
|
233
|
+
const nTo = resolve(path, name)
|
|
234
|
+
const newTrans1 = {
|
|
235
|
+
...copy(transfer),
|
|
236
|
+
toPathReal: transfer.toPath,
|
|
237
|
+
fromPathReal: transfer.fromPath,
|
|
238
|
+
toPath: nTo,
|
|
239
|
+
fromPath: p,
|
|
240
|
+
originalId: transfer.id,
|
|
241
|
+
id: generate()
|
|
242
|
+
}
|
|
243
|
+
delete newTrans1.fromFile
|
|
244
|
+
delete newTrans1.inited
|
|
245
|
+
delete newTrans1.zip
|
|
246
|
+
const newTrans2 = copy(newTrans1)
|
|
247
|
+
newTrans2.unzip = true
|
|
248
|
+
newTrans2.id = generate()
|
|
249
|
+
newTrans1.next = newTrans2
|
|
250
|
+
this.insert([newTrans1])
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
buildUnzipPath = (transfer) => {
|
|
254
|
+
const {
|
|
255
|
+
newName,
|
|
256
|
+
toPath,
|
|
257
|
+
typeTo,
|
|
258
|
+
oldName
|
|
259
|
+
} = transfer
|
|
260
|
+
const isToRemote = typeTo === typeMap.remote
|
|
261
|
+
const { path } = getFolderFromFilePath(toPath, isToRemote)
|
|
262
|
+
const np = newName
|
|
263
|
+
? resolve(path, 'temp-' + newName)
|
|
264
|
+
: path
|
|
265
|
+
return {
|
|
266
|
+
targetPath: path,
|
|
267
|
+
path: np,
|
|
268
|
+
name: oldName
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
unzipFile = async () => {
|
|
273
|
+
if (this.unzipping) {
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
276
|
+
this.unzipping = true
|
|
277
|
+
const { transfer } = this.props
|
|
278
|
+
const {
|
|
279
|
+
fromPath,
|
|
280
|
+
toPath,
|
|
281
|
+
typeTo,
|
|
282
|
+
newName,
|
|
283
|
+
sessionId
|
|
284
|
+
} = transfer
|
|
285
|
+
const isToRemote = typeTo === typeMap.remote
|
|
286
|
+
const {
|
|
287
|
+
path,
|
|
288
|
+
name,
|
|
289
|
+
targetPath
|
|
290
|
+
} = this.buildUnzipPath(transfer)
|
|
291
|
+
if (isToRemote) {
|
|
292
|
+
if (newName) {
|
|
293
|
+
await mkdirCmd('', sessionId, path)
|
|
294
|
+
await delay(1000)
|
|
295
|
+
}
|
|
296
|
+
await unzipCmd('', sessionId, toPath, path)
|
|
297
|
+
if (newName) {
|
|
298
|
+
const mvFrom = resolve(path, name)
|
|
299
|
+
const mvTo = resolve(targetPath, newName)
|
|
300
|
+
await mvCmd('', sessionId, mvFrom, mvTo)
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
if (newName) {
|
|
304
|
+
await fs.mkdir(path)
|
|
305
|
+
}
|
|
306
|
+
await fs.unzipFile(toPath, path)
|
|
307
|
+
if (newName) {
|
|
308
|
+
const mvFrom = resolve(path, name)
|
|
309
|
+
const mvTo = resolve(targetPath, newName)
|
|
310
|
+
await fs.mv(mvFrom, mvTo)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
await rmCmd('', sessionId, !isToRemote ? fromPath : toPath)
|
|
314
|
+
await fs.rmrf(!isToRemote ? toPath : fromPath)
|
|
315
|
+
if (newName) {
|
|
316
|
+
if (isToRemote) {
|
|
317
|
+
await rmCmd('', sessionId, path)
|
|
318
|
+
} else {
|
|
319
|
+
await fs.rmrf(path)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
this.onEnd()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
doTransfer = async () => {
|
|
326
|
+
const { transfer } = this.props
|
|
327
|
+
const {
|
|
328
|
+
fromPath,
|
|
329
|
+
toPath,
|
|
330
|
+
typeFrom,
|
|
331
|
+
fromFile: {
|
|
332
|
+
mode: fromMode
|
|
333
|
+
},
|
|
334
|
+
toFile = {}
|
|
335
|
+
} = transfer
|
|
336
|
+
const transferType = typeFrom === typeMap.local ? transferTypeMap.upload : transferTypeMap.download
|
|
337
|
+
const isDown = transferType === transferTypeMap.download
|
|
338
|
+
const localPath = isDown
|
|
339
|
+
? toPath
|
|
340
|
+
: fromPath
|
|
341
|
+
const remotePath = isDown
|
|
342
|
+
? fromPath
|
|
343
|
+
: toPath
|
|
344
|
+
const mode = toFile.mode || fromMode
|
|
345
|
+
const sftp = window.sftps[this.sessionId]
|
|
346
|
+
this.inst.transport = await sftp[transferType]({
|
|
347
|
+
remotePath,
|
|
348
|
+
localPath,
|
|
349
|
+
options: { mode },
|
|
350
|
+
onData: this.onData,
|
|
351
|
+
onError: this.onError,
|
|
352
|
+
onEnd: this.onEnd
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
isTransferAction = (action) => {
|
|
357
|
+
return action.includes('rename') || action === 'transfer'
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
initTransfer = async () => {
|
|
361
|
+
if (this.inst.started) {
|
|
362
|
+
return
|
|
363
|
+
}
|
|
364
|
+
const { transfer } = this.props
|
|
365
|
+
const {
|
|
366
|
+
typeFrom,
|
|
367
|
+
typeTo,
|
|
368
|
+
fromFile: {
|
|
369
|
+
isDirectory
|
|
370
|
+
},
|
|
371
|
+
action,
|
|
372
|
+
expanded,
|
|
373
|
+
zip,
|
|
374
|
+
unzip,
|
|
375
|
+
inited
|
|
376
|
+
} = transfer
|
|
377
|
+
const t = Date.now()
|
|
378
|
+
this.update({
|
|
379
|
+
startTime: t
|
|
380
|
+
})
|
|
381
|
+
this.inst.startTime = t
|
|
382
|
+
this.inst.started = true
|
|
383
|
+
if (unzip && inited) {
|
|
384
|
+
this.unzipFile()
|
|
385
|
+
} else if (zip && inited) {
|
|
386
|
+
this.zipTransfer()
|
|
387
|
+
} else if (typeFrom === typeTo) {
|
|
388
|
+
return this.mvOrCp()
|
|
389
|
+
} else if (isDirectory && expanded && this.isTransferAction(action)) {
|
|
390
|
+
return this.mkdir()
|
|
391
|
+
.then(this.onEnd)
|
|
392
|
+
.catch(this.onError)
|
|
393
|
+
} else if (!isDirectory) {
|
|
394
|
+
this.doTransfer()
|
|
395
|
+
} else if (expanded && isDirectory && !this.isTransferAction(action)) {
|
|
396
|
+
this.cancel()
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
onError = (e) => {
|
|
401
|
+
const up = {
|
|
402
|
+
status: 'exception',
|
|
403
|
+
error: e.message
|
|
404
|
+
}
|
|
405
|
+
this.onEnd(up)
|
|
406
|
+
window.store.onError(e)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
mkdir = async () => {
|
|
410
|
+
const {
|
|
411
|
+
transfer
|
|
412
|
+
} = this.props
|
|
413
|
+
const {
|
|
414
|
+
typeTo,
|
|
415
|
+
toPath,
|
|
416
|
+
sessionId
|
|
417
|
+
} = transfer
|
|
418
|
+
if (typeTo === typeMap.local) {
|
|
419
|
+
return fs.mkdir(toPath)
|
|
420
|
+
.catch(this.onError)
|
|
421
|
+
}
|
|
422
|
+
const sftp = window.sftps[sessionId]
|
|
423
|
+
return sftp.mkdir(toPath)
|
|
424
|
+
.catch(this.onError)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
render () {
|
|
428
|
+
return null
|
|
429
|
+
}
|
|
430
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pass transfer list from props
|
|
3
|
+
* when list changes, do transfer and other op
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Component } from '../common/react-subx'
|
|
7
|
+
import Transports from './transports-ui-store'
|
|
8
|
+
import { maxTransport } from '../../common/constants'
|
|
9
|
+
|
|
10
|
+
export default class TransportsActionStore extends Component {
|
|
11
|
+
componentDidMount () {
|
|
12
|
+
this.control()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
componentDidUpdate (prevProps) {
|
|
16
|
+
if (
|
|
17
|
+
prevProps._fileTransfers !== this.props._fileTransfers
|
|
18
|
+
) {
|
|
19
|
+
this.control()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
control = async () => {
|
|
24
|
+
const { store } = this.props
|
|
25
|
+
let {
|
|
26
|
+
fileTransfers
|
|
27
|
+
} = store
|
|
28
|
+
|
|
29
|
+
fileTransfers = fileTransfers.map(t => {
|
|
30
|
+
const {
|
|
31
|
+
typeTo,
|
|
32
|
+
typeFrom,
|
|
33
|
+
fromFile,
|
|
34
|
+
inited
|
|
35
|
+
} = t
|
|
36
|
+
const ready = !!fromFile
|
|
37
|
+
if (typeTo === typeFrom && ready && !inited) {
|
|
38
|
+
t.inited = true
|
|
39
|
+
}
|
|
40
|
+
return t
|
|
41
|
+
})
|
|
42
|
+
// if (pauseAllTransfer) {
|
|
43
|
+
// return store.setFileTransfers(fileTransfers)
|
|
44
|
+
// }
|
|
45
|
+
let count = fileTransfers.filter(t => {
|
|
46
|
+
const {
|
|
47
|
+
typeTo,
|
|
48
|
+
typeFrom,
|
|
49
|
+
inited
|
|
50
|
+
} = t
|
|
51
|
+
return typeTo !== typeFrom && inited
|
|
52
|
+
}).length
|
|
53
|
+
if (count >= maxTransport) {
|
|
54
|
+
return store.setFileTransfers(fileTransfers)
|
|
55
|
+
}
|
|
56
|
+
const len = fileTransfers.length
|
|
57
|
+
const ids = []
|
|
58
|
+
for (let i = 0; i < len; i++) {
|
|
59
|
+
const tr = fileTransfers[i]
|
|
60
|
+
const {
|
|
61
|
+
typeTo,
|
|
62
|
+
typeFrom,
|
|
63
|
+
inited,
|
|
64
|
+
fromFile,
|
|
65
|
+
error,
|
|
66
|
+
id,
|
|
67
|
+
action
|
|
68
|
+
} = tr
|
|
69
|
+
if (!error) {
|
|
70
|
+
ids.push(id)
|
|
71
|
+
}
|
|
72
|
+
const isTransfer = typeTo !== typeFrom
|
|
73
|
+
const ready = (
|
|
74
|
+
action && fromFile
|
|
75
|
+
)
|
|
76
|
+
if (
|
|
77
|
+
!ready ||
|
|
78
|
+
inited ||
|
|
79
|
+
!isTransfer
|
|
80
|
+
) {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
// if (isTransfer && tr.fromFile.isDirectory) {
|
|
84
|
+
// i = len
|
|
85
|
+
// continue
|
|
86
|
+
// }
|
|
87
|
+
if (
|
|
88
|
+
fromFile && count < maxTransport
|
|
89
|
+
) {
|
|
90
|
+
count++
|
|
91
|
+
tr.inited = true
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
store.setFileTransfers(fileTransfers)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
render () {
|
|
98
|
+
return (
|
|
99
|
+
<Transports {...this.props} />
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transporter UI component
|
|
3
|
+
*/
|
|
4
|
+
import { Component } from '../common/react-subx'
|
|
5
|
+
import Transport from './transport-action-store'
|
|
6
|
+
|
|
7
|
+
export default class TransportsUI extends Component {
|
|
8
|
+
render () {
|
|
9
|
+
const { store } = this.props
|
|
10
|
+
const {
|
|
11
|
+
fileTransfers
|
|
12
|
+
} = store
|
|
13
|
+
if (!fileTransfers.length) {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
return fileTransfers.map((t, i) => {
|
|
17
|
+
const { id } = t
|
|
18
|
+
return (
|
|
19
|
+
<Transport
|
|
20
|
+
{...this.props}
|
|
21
|
+
transfer={t}
|
|
22
|
+
inited={t.inited}
|
|
23
|
+
cancel={t.cancel}
|
|
24
|
+
pause={t.pausing}
|
|
25
|
+
key={id + ':tr:' + i}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
PauseCircleOutlined
|
|
9
9
|
} from '@ant-design/icons'
|
|
10
10
|
import { get } from 'lodash-es'
|
|
11
|
-
import {
|
|
12
|
-
transportTypes
|
|
13
|
-
} from '../sftp/transport-types'
|
|
14
|
-
import postMessage from '../../common/post-msg'
|
|
15
11
|
|
|
16
12
|
const { Option } = Select
|
|
17
13
|
|
|
@@ -28,21 +24,16 @@ export default class TransferModalUI extends Component {
|
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
handlePauseOrResumeAll = () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
id: this.state.filter
|
|
34
|
-
})
|
|
27
|
+
const { store } = window
|
|
28
|
+
store.pauseAllTransfer ? store.resumeAll() : store.pauseAll()
|
|
35
29
|
}
|
|
36
30
|
|
|
37
31
|
handleCancelAll = () => {
|
|
38
|
-
|
|
39
|
-
action: transportTypes.cancelAll,
|
|
40
|
-
id: this.state.filter
|
|
41
|
-
})
|
|
32
|
+
window.store.cancelAll()
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
getGroups = () => {
|
|
45
|
-
const fileTransfers = this.props.store.
|
|
36
|
+
const fileTransfers = this.props.store.fileTransfers
|
|
46
37
|
const tree = fileTransfers.reduce((p, k) => {
|
|
47
38
|
const {
|
|
48
39
|
id,
|
|
@@ -79,7 +70,7 @@ export default class TransferModalUI extends Component {
|
|
|
79
70
|
const {
|
|
80
71
|
filter
|
|
81
72
|
} = this.state
|
|
82
|
-
const fileTransfers = this.props.store.
|
|
73
|
+
const fileTransfers = this.props.store.fileTransfers
|
|
83
74
|
return filter === 'all'
|
|
84
75
|
? fileTransfers
|
|
85
76
|
: fileTransfers.filter(d => d.sessionId === filter)
|
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
PlayCircleOutlined,
|
|
9
9
|
PauseCircleOutlined
|
|
10
10
|
} from '@ant-design/icons'
|
|
11
|
-
import {
|
|
12
|
-
transportTypes
|
|
13
|
-
} from '../sftp/transport-types'
|
|
14
|
-
import postMessage from '../../common/post-msg'
|
|
15
11
|
import './transfer.styl'
|
|
16
12
|
|
|
17
13
|
const { prefix } = window
|
|
@@ -35,16 +31,10 @@ export default function Transporter (props) {
|
|
|
35
31
|
id
|
|
36
32
|
} = props.transfer
|
|
37
33
|
function cancel () {
|
|
38
|
-
|
|
39
|
-
action: transportTypes.cancelTransport,
|
|
40
|
-
id
|
|
41
|
-
})
|
|
34
|
+
window.store.cancelTransfer(id)
|
|
42
35
|
}
|
|
43
36
|
function handlePauseOrResume () {
|
|
44
|
-
|
|
45
|
-
action: transportTypes.pauseOrResumeTransfer,
|
|
46
|
-
id
|
|
47
|
-
})
|
|
37
|
+
window.store.toggleTransfer(id)
|
|
48
38
|
}
|
|
49
39
|
const isTransfer = typeTo !== typeFrom
|
|
50
40
|
const Icon = !pausing ? PauseCircleOutlined : PlayCircleOutlined
|
|
@@ -26,6 +26,7 @@ import { shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
|
|
|
26
26
|
const { prefix } = window
|
|
27
27
|
const e = prefix('tabs')
|
|
28
28
|
const m = prefix('menu')
|
|
29
|
+
const s = prefix('sftp')
|
|
29
30
|
const onDragCls = 'ondrag-tab'
|
|
30
31
|
const onDragOverCls = 'dragover-tab'
|
|
31
32
|
|
|
@@ -346,6 +347,36 @@ class Tab extends Component {
|
|
|
346
347
|
)
|
|
347
348
|
}
|
|
348
349
|
|
|
350
|
+
// sshTunnelResults is a array of { sshTunnel, error? }, sshTunnel is a object has props of sshTunnelLocalPort, sshTunnelRemoteHost, sshTunnelRemotePort, sshTunnel, sshTunnelLocalHost, should build sshTunnel string from sshTunnel object, when error exist, this string should start with "error:", return title and sshTunnelResults list in react element.
|
|
351
|
+
renderTitle = (sshTunnelResults, title) => {
|
|
352
|
+
const list = sshTunnelResults.map(({ sshTunnel: obj, error }, i) => {
|
|
353
|
+
const {
|
|
354
|
+
sshTunnelLocalPort,
|
|
355
|
+
sshTunnelRemoteHost = '127.0.0.1',
|
|
356
|
+
sshTunnelRemotePort,
|
|
357
|
+
sshTunnel,
|
|
358
|
+
sshTunnelLocalHost = '127.0.0.1',
|
|
359
|
+
name
|
|
360
|
+
} = obj
|
|
361
|
+
let tunnel = sshTunnel === 'forwardRemoteToLocal'
|
|
362
|
+
? `-> ${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort} -> ${sshTunnelLocalHost}:${sshTunnelLocalPort}`
|
|
363
|
+
: `-> ${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort} -> ${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
|
|
364
|
+
if (error) {
|
|
365
|
+
tunnel = `error: ${tunnel}`
|
|
366
|
+
}
|
|
367
|
+
if (name) {
|
|
368
|
+
tunnel = `[${name}] ${tunnel}`
|
|
369
|
+
}
|
|
370
|
+
return <div key={tunnel}>{tunnel}</div>
|
|
371
|
+
})
|
|
372
|
+
return (
|
|
373
|
+
<div>
|
|
374
|
+
<div>${title}</div>
|
|
375
|
+
{list}
|
|
376
|
+
</div>
|
|
377
|
+
)
|
|
378
|
+
}
|
|
379
|
+
|
|
349
380
|
renderCloseIcon () {
|
|
350
381
|
return (
|
|
351
382
|
<span className='tab-close pointer'>
|
|
@@ -360,7 +391,13 @@ class Tab extends Component {
|
|
|
360
391
|
} = this.props
|
|
361
392
|
const { isLast } = this.props
|
|
362
393
|
const { tab, terminalOnData } = this.state
|
|
363
|
-
const {
|
|
394
|
+
const {
|
|
395
|
+
id,
|
|
396
|
+
isEditting,
|
|
397
|
+
status,
|
|
398
|
+
isTransporting,
|
|
399
|
+
sshTunnelResults
|
|
400
|
+
} = tab
|
|
364
401
|
const active = id === currentTabId
|
|
365
402
|
const cls = classnames(
|
|
366
403
|
`tab-${id}`,
|
|
@@ -378,6 +415,10 @@ class Tab extends Component {
|
|
|
378
415
|
}
|
|
379
416
|
)
|
|
380
417
|
const title = createName(tab)
|
|
418
|
+
let tooltipTitle = title
|
|
419
|
+
if (sshTunnelResults) {
|
|
420
|
+
tooltipTitle = this.renderTitle(sshTunnelResults, title)
|
|
421
|
+
}
|
|
381
422
|
if (isEditting) {
|
|
382
423
|
return this.renderEditting(tab, cls)
|
|
383
424
|
}
|
|
@@ -387,7 +428,7 @@ class Tab extends Component {
|
|
|
387
428
|
: {}
|
|
388
429
|
return (
|
|
389
430
|
<Tooltip
|
|
390
|
-
title={
|
|
431
|
+
title={tooltipTitle}
|
|
391
432
|
placement='top'
|
|
392
433
|
>
|
|
393
434
|
<div
|