@electerm/electerm-react 1.38.65 → 1.38.80

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 (43) hide show
  1. package/client/common/constants.js +6 -3
  2. package/client/common/create-title.jsx +9 -1
  3. package/client/common/sftp.js +3 -0
  4. package/client/components/batch-op/batch-op.jsx +1 -6
  5. package/client/components/bookmark-form/bookmark-form.styl +3 -1
  6. package/client/components/bookmark-form/index.jsx +12 -8
  7. package/client/components/bookmark-form/render-ssh-tunnel.jsx +210 -88
  8. package/client/components/bookmark-form/ssh-form-ui.jsx +1 -1
  9. package/client/components/bookmark-form/web-form-ui.jsx +96 -0
  10. package/client/components/bookmark-form/web-form.jsx +16 -0
  11. package/client/components/main/main.jsx +14 -0
  12. package/client/components/quick-commands/qm.styl +1 -1
  13. package/client/components/session/session.styl +4 -1
  14. package/client/components/session/sessions.jsx +16 -2
  15. package/client/components/session/web-session.jsx +20 -0
  16. package/client/components/sftp/{confirm-modal.jsx → confirm-modal-store.jsx} +81 -50
  17. package/client/components/sftp/file-item.jsx +2 -0
  18. package/client/components/sftp/sftp-entry.jsx +27 -37
  19. package/client/components/sftp/transfer-conflict-store.jsx +291 -0
  20. package/client/components/sftp/transport-action-store.jsx +430 -0
  21. package/client/components/sftp/transports-action-store.jsx +102 -0
  22. package/client/components/sftp/transports-ui-store.jsx +30 -0
  23. package/client/components/sidebar/transfer-list-control.jsx +5 -14
  24. package/client/components/sidebar/transport-ui.jsx +2 -12
  25. package/client/components/tabs/tab.jsx +43 -2
  26. package/client/components/tabs/tabs.styl +1 -1
  27. package/client/components/terminal/index.jsx +14 -1
  28. package/client/components/terminal/terminal-interactive.jsx +15 -0
  29. package/client/components/terminal-info/disk.jsx +9 -0
  30. package/client/entry/worker.js +5 -1
  31. package/client/store/index.js +16 -1
  32. package/client/store/init-state.js +2 -3
  33. package/client/store/sync.js +5 -2
  34. package/client/store/tab.js +1 -1
  35. package/client/store/transfer-list.js +55 -2
  36. package/client/store/watch.js +0 -8
  37. package/package.json +1 -1
  38. package/client/components/sftp/transfer-conflict.jsx +0 -323
  39. package/client/components/sftp/transport-action.jsx +0 -412
  40. package/client/components/sftp/transport-entry.jsx +0 -108
  41. package/client/components/sftp/transport-types.js +0 -8
  42. package/client/components/sftp/transports-action.jsx +0 -111
  43. package/client/components/sftp/transports-ui.jsx +0 -93
@@ -1,323 +0,0 @@
1
- /**
2
- * pass transfer list from props
3
- * when list changes, do transfer and other op
4
- */
5
-
6
- import { useRef } from 'react'
7
- import { useDelta, useConditionalEffect } from 'react-delta'
8
- import { maxTransport, typeMap } from '../../common/constants'
9
- import {
10
- getLocalFileInfo,
11
- getRemoteFileInfo,
12
- getFolderFromFilePath,
13
- getFileExt,
14
- checkFolderSize
15
- } from './file-read'
16
- import copy from 'json-deep-copy'
17
- import { findIndex, find } from 'lodash-es'
18
- import generate from '../../common/uid'
19
- import resolve from '../../common/resolve'
20
- import eq from 'fast-deep-equal'
21
- import { createTransferProps } from './transfer-common'
22
-
23
- export default (props) => {
24
- const { transferList, sessionId } = props
25
- const delta = useDelta(transferList)
26
- const currentId = useRef('')
27
- const timer = useRef(null)
28
- const onConfirm = useRef(false)
29
- const ref = {}
30
-
31
- ref.localCheckExist = (path) => {
32
- return getLocalFileInfo(path)
33
- }
34
-
35
- ref.remoteCheckExist = (path) => {
36
- const { sftp } = props
37
- return getRemoteFileInfo(sftp, path)
38
- .then(r => r)
39
- .catch(() => false)
40
- }
41
-
42
- const checkExist = (type, path) => {
43
- return ref[type + 'CheckExist'](path)
44
- }
45
-
46
- function rename (tr, action, _renameId) {
47
- const isRemote = tr.typeTo === typeMap.remote
48
- const { path, name } = getFolderFromFilePath(tr.toPath, isRemote)
49
- const { base, ext } = getFileExt(name)
50
- const renameId = _renameId || generate()
51
- const newName = ext
52
- ? `${base}(rename-${renameId}).${ext}`
53
- : `${base}(rename-${renameId})`
54
- const res = {
55
- ...tr,
56
- renameId,
57
- newName,
58
- oldName: base,
59
- toPath: resolve(path, newName)
60
- }
61
- if (action) {
62
- res.action = action
63
- }
64
- return res
65
- }
66
-
67
- function updateTransferAction (data) {
68
- const {
69
- id,
70
- action,
71
- transfer
72
- } = data
73
- const {
74
- fromFile
75
- } = transfer
76
- clear()
77
- props.modifier((old) => {
78
- let transferList = copy(old.transferList)
79
- const index = findIndex(transferList, d => d.id === id)
80
- if (index < 0) {
81
- return {
82
- transferList
83
- }
84
- }
85
- transferList[index].fromFile = fromFile
86
- transferList[index].action = action
87
- if (action === 'skip') {
88
- transferList.splice(index, 1)
89
- } else if (action === 'cancel') {
90
- transferList = transferList.slice(0, index)
91
- }
92
- if (action.includes('All')) {
93
- transferList = transferList.map((t, i) => {
94
- if (i < index) {
95
- return t
96
- }
97
- return {
98
- ...t,
99
- action: action.replace('All', '')
100
- }
101
- })
102
- }
103
- if (action.includes('rename')) {
104
- transferList[index] = rename(transferList[index])
105
- } else if (action === 'skipAll') {
106
- transferList.splice(index, 1)
107
- }
108
- window.store.setTransfers(transferList, sessionId)
109
- return {
110
- transferList
111
- }
112
- })
113
- }
114
-
115
- function tagTransferError (id, errorMsg) {
116
- const tr = find(transferList, d => d.id === id)
117
- window.store.addTransferHistory({
118
- ...tr,
119
- host: props.host,
120
- error: errorMsg,
121
- finishTime: Date.now()
122
- })
123
- props.modifier(old => {
124
- const transferList = copy(old.transferList)
125
- const index = findIndex(transferList, d => d.id === id)
126
- if (index >= 0) {
127
- transferList.splice(index, 1)
128
- }
129
- window.store.setTransfers(transferList, sessionId)
130
- return {
131
- transferList
132
- }
133
- })
134
- }
135
-
136
- function setConflict (tr) {
137
- if (props.transferToConfirm) {
138
- return
139
- }
140
- props.modifier({
141
- transferToConfirm: tr
142
- })
143
- }
144
-
145
- function onDecision (event) {
146
- if (event && event.data && event.data.id === currentId.current) {
147
- // const {
148
- // transferGroupId,
149
- // fileId,
150
- // id,
151
- // action
152
- // } = event.data
153
- currentId.current = ''
154
- updateTransferAction(event.data)
155
- onConfirm.current = false
156
- window.removeEventListener('message', onDecision)
157
- }
158
- }
159
-
160
- function waitForSignal () {
161
- window.addEventListener('message', onDecision)
162
- }
163
-
164
- function setCanTransfer (fromFile, tr) {
165
- clear()
166
- props.modifier((old) => {
167
- const transferList = copy(old.transferList)
168
- const index = findIndex(transferList, t => {
169
- return t.id === tr.id
170
- })
171
- if (index >= 0) {
172
- const up = {
173
- action: 'transfer',
174
- fromFile
175
- }
176
- Object.assign(transferList[index], up)
177
- }
178
- window.store.setTransfers(transferList, sessionId)
179
- return {
180
- transferList
181
- }
182
- })
183
- }
184
-
185
- async function expand (fromFile, tr) {
186
- let list = []
187
- if (!tr.skipExpand) {
188
- const { type } = fromFile
189
- list = await props[type + 'List'](
190
- true, tr.fromPath
191
- )
192
- list = list.map(t => {
193
- return {
194
- typeFrom: tr.typeFrom,
195
- typeTo: tr.typeTo,
196
- fromPath: resolve(t.path, t.name),
197
- toPath: resolve(tr.toPath, t.name),
198
- id: generate(),
199
- ...createTransferProps(props),
200
- parentId: tr.id
201
- }
202
- })
203
- }
204
- clear()
205
- props.modifier((old) => {
206
- const transferList = copy(old.transferList)
207
- const index = findIndex(transferList, t => {
208
- return t.id === tr.id
209
- })
210
- transferList.splice(index + 1, 0, ...list)
211
- if (transferList[index]) {
212
- transferList[index].expanded = true
213
- }
214
- window.store.setTransfers(transferList, sessionId)
215
- return {
216
- transferList
217
- }
218
- })
219
- }
220
-
221
- function clear () {
222
- currentId.current = ''
223
- }
224
- async function watchFile () {
225
- if (!transferList.length && currentId.current) {
226
- return clear()
227
- }
228
- const tr = transferList
229
- .filter(t => {
230
- return (
231
- !t.action ||
232
- !t.fromFile ||
233
- (t.fromFile.isDirectory && !t.expanded)
234
- )
235
- })[0]
236
- if (!tr) {
237
- onConfirm.current = false
238
- return clear()
239
- }
240
- if (currentId.current) {
241
- return null
242
- }
243
- currentId.current = tr.id
244
- const {
245
- typeFrom,
246
- typeTo,
247
- fromPath,
248
- toPath,
249
- id,
250
- action,
251
- expanded,
252
- renameId,
253
- parentId,
254
- skipConfirm
255
- } = tr
256
- const fromFile = tr.fromFile
257
- ? tr.fromFile
258
- : await checkExist(typeFrom, fromPath)
259
- if (!fromFile) {
260
- currentId.current = ''
261
- return tagTransferError(id, 'file not exist')
262
- }
263
- let toFile = false
264
- if (renameId || parentId) {
265
- toFile = false
266
- } else if (fromPath === toPath && typeFrom === typeTo) {
267
- toFile = true
268
- } else {
269
- toFile = await checkExist(typeTo, toPath)
270
- }
271
- if (fromFile.isDirectory) {
272
- const skip = await checkFolderSize(props, fromFile)
273
- .then(d => d && typeFrom !== typeTo)
274
- tr.zip = skip
275
- tr.skipExpand = skip
276
- }
277
- if (fromPath === toPath && typeFrom === typeTo) {
278
- return updateTransferAction({
279
- id,
280
- action: 'rename',
281
- transfer: {
282
- ...tr,
283
- operation: 'cp',
284
- fromFile
285
- }
286
- })
287
- } else if (toFile && !action && !skipConfirm) {
288
- waitForSignal(id)
289
- if (!onConfirm.current) {
290
- onConfirm.current = true
291
- return setConflict({
292
- ...tr,
293
- fromFile,
294
- toFile
295
- })
296
- }
297
- } else if (toFile && !tr.fromFile && action) {
298
- return updateTransferAction({
299
- id,
300
- action,
301
- transfer: {
302
- ...tr,
303
- fromFile
304
- }
305
- })
306
- } else if (
307
- fromFile.isDirectory &&
308
- !expanded &&
309
- typeFrom !== typeTo &&
310
- transferList.filter(t => t.fromFile).length < maxTransport
311
- ) {
312
- return expand(fromFile, tr)
313
- }
314
- setCanTransfer(fromFile, tr)
315
- }
316
- useConditionalEffect(() => {
317
- watchFile()
318
- return () => {
319
- clearTimeout(timer.current)
320
- }
321
- }, delta && delta.prev && !eq(delta.prev, delta.curr))
322
- return null
323
- }
@@ -1,412 +0,0 @@
1
- import { useEffect, useRef } from 'react'
2
- import { useDelta, useConditionalEffect } from 'react-delta'
3
- import copy from 'json-deep-copy'
4
- import { findIndex, isFunction, noop } from 'lodash-es'
5
- import generate from '../../common/uid'
6
- import { typeMap, transferTypeMap } from '../../common/constants'
7
- import fs from '../../common/fs'
8
- import { transportTypes } from './transport-types'
9
- import format, { computeLeftTime, computePassedTime } from './transfer-speed-format'
10
- import { getFolderFromFilePath } from './file-read'
11
- import resolve from '../../common/resolve'
12
- import delay from '../../common/wait'
13
- import { zipCmd, unzipCmd, rmCmd, mvCmd, mkdirCmd } from './zip'
14
- import './transfer.styl'
15
-
16
- export default function transportAction (props) {
17
- const { transfer } = props
18
- const inst = useRef({})
19
- const unzipping = useRef(false)
20
- const initRef = useDelta(transfer.inited)
21
- const initRefExpand = useDelta(transfer.expaned)
22
- function update (up) {
23
- props.modifier((old) => {
24
- const transferList = copy(old.transferList)
25
- const index = findIndex(transferList, t => t.id === transfer.id)
26
- if (index < 0) {
27
- return {
28
- transferList
29
- }
30
- }
31
- window.store.editTransfer(
32
- transferList[index].id,
33
- up
34
- )
35
- Object.assign(transferList[index], up)
36
- return {
37
- transferList
38
- }
39
- })
40
- }
41
- function insert (insts) {
42
- props.modifier((old) => {
43
- const transferList = copy(old.transferList)
44
- const index = findIndex(transferList, t => t.id === transfer.id)
45
- transferList.splice(index, 1, ...insts)
46
- window.store.setTransfers(transferList, transfer.sessionId)
47
- return {
48
- transferList
49
- }
50
- })
51
- }
52
- function onEnd (update = {}) {
53
- if (inst.current.onCancel) {
54
- return
55
- }
56
- const {
57
- typeTo,
58
- next
59
- } = transfer
60
- const cb = props[typeTo + 'List']
61
- const finishTime = Date.now()
62
- if (!props.config.disableTransferHistory) {
63
- window.store.addTransferHistory(
64
- {
65
- ...transfer,
66
- ...update,
67
- finishTime,
68
- startTime: inst.current.startTime,
69
- size: transfer.fromFile.size,
70
- speed: format(transfer.fromFile.size, inst.current.startTime),
71
- host: props.tab.host
72
- }
73
- )
74
- }
75
- if (next) {
76
- insert([copy(next)])
77
- }
78
- cancel(cb)
79
- }
80
- function onData (transferred) {
81
- if (inst.current.onCancel) {
82
- return
83
- }
84
- const up = {}
85
- const total = transfer.fromFile.size
86
- let percent = total === 0
87
- ? 0
88
- : Math.floor(100 * transferred / total)
89
- percent = percent >= 100 ? 99 : percent
90
- up.percent = percent
91
- up.status = 'active'
92
- up.transferred = transferred
93
- up.startTime = inst.current.startTime
94
- up.speed = format(transferred, up.startTime)
95
- Object.assign(
96
- up,
97
- computeLeftTime(transferred, total, up.startTime)
98
- )
99
- up.passedTime = computePassedTime(up.startTime)
100
- update(up)
101
- }
102
- function cancel (callback) {
103
- if (inst.current.onCancel) {
104
- return
105
- }
106
- inst.current.onCancel = true
107
- const { id } = transfer
108
- inst.current.transport && inst.current.transport.destroy()
109
- props.modifier((old) => {
110
- const oldTrans = copy(old.transferList)
111
- const transferList = oldTrans.filter(t => {
112
- return t.id !== id
113
- })
114
- window.store.setTransfers(transferList, transfer.sessionId)
115
- return {
116
- transferList
117
- }
118
- }, isFunction(callback) ? callback : noop)
119
- }
120
-
121
- function pause () {
122
- inst.current.transport && inst.current.transport.pause()
123
- update({
124
- pausing: true
125
- })
126
- }
127
-
128
- function resume () {
129
- update({
130
- pausing: false
131
- })
132
- inst.current.transport && inst.current.transport.resume()
133
- }
134
-
135
- function handlePauseOrResume () {
136
- if (transfer.pausing) {
137
- resume()
138
- } else {
139
- pause()
140
- }
141
- }
142
-
143
- function onMessage (e) {
144
- const action = e?.data?.action
145
- const id = e?.data?.id
146
- const ids = e?.data?.ids
147
- if (id === transfer.id) {
148
- switch (action) {
149
- case transportTypes.cancelTransport:
150
- cancel()
151
- break
152
- case transportTypes.pauseOrResumeTransfer:
153
- handlePauseOrResume()
154
- break
155
- default:
156
- break
157
- }
158
- }
159
- if (
160
- (ids && ids.includes(transfer.id)) ||
161
- (ids && ids.length === 0)
162
- ) {
163
- if (
164
- action === transportTypes.pauseTransport
165
- ) {
166
- pause()
167
- } else if (action === transportTypes.resumeTransport) {
168
- resume()
169
- } else if (action === transportTypes.cancelTransport) {
170
- cancel()
171
- }
172
- }
173
- }
174
- function initEvent () {
175
- window.addEventListener('message', onMessage)
176
- }
177
- function onDestroy () {
178
- window.removeEventListener('message', onMessage)
179
- }
180
- function mvOrCp () {
181
- const {
182
- fromPath,
183
- toPath,
184
- typeFrom,
185
- operation // 'mv' or 'cp'
186
- } = transfer
187
- if (typeFrom === typeMap.local) {
188
- return fs[operation](fromPath, toPath)
189
- .then(onEnd)
190
- .catch(e => {
191
- onEnd()
192
- onError(e)
193
- })
194
- }
195
- return props.sftp[operation](fromPath, toPath)
196
- .then(onEnd)
197
- .catch(e => {
198
- onEnd()
199
- onError(e)
200
- })
201
- }
202
- async function zipTransfer () {
203
- const {
204
- fromPath,
205
- toPath,
206
- typeFrom
207
- } = transfer
208
- let p
209
- let isFromRemote
210
- if (typeFrom === typeMap.local) {
211
- isFromRemote = false
212
- p = await fs.zipFolder(fromPath)
213
- } else {
214
- isFromRemote = true
215
- p = await zipCmd(props.pid, props.sessionId, fromPath)
216
- }
217
- const { name } = getFolderFromFilePath(p, isFromRemote)
218
- const { path } = getFolderFromFilePath(toPath, !isFromRemote)
219
- const nTo = resolve(path, name)
220
- const newTrans1 = {
221
- ...copy(transfer),
222
- toPathReal: transfer.toPath,
223
- fromPathReal: transfer.fromPath,
224
- toPath: nTo,
225
- fromPath: p,
226
- originalId: transfer.id,
227
- id: generate()
228
- }
229
- delete newTrans1.fromFile
230
- delete newTrans1.inited
231
- delete newTrans1.zip
232
- const newTrans2 = copy(newTrans1)
233
- newTrans2.unzip = true
234
- newTrans2.id = generate()
235
- newTrans1.next = newTrans2
236
- insert([newTrans1])
237
- }
238
-
239
- function buildUnzipPath (transfer) {
240
- const {
241
- newName,
242
- toPath,
243
- typeTo,
244
- oldName
245
- } = transfer
246
- const isToRemote = typeTo === typeMap.remote
247
- const { path } = getFolderFromFilePath(toPath, isToRemote)
248
- const np = newName
249
- ? resolve(path, 'temp-' + newName)
250
- : path
251
- return {
252
- targetPath: path,
253
- path: np,
254
- name: oldName
255
- }
256
- }
257
-
258
- async function unzipFile () {
259
- if (unzipping.current) {
260
- return false
261
- }
262
- unzipping.current = true
263
- const {
264
- fromPath,
265
- toPath,
266
- typeTo,
267
- newName
268
- } = transfer
269
- const isToRemote = typeTo === typeMap.remote
270
- const {
271
- path,
272
- name,
273
- targetPath
274
- } = buildUnzipPath(transfer)
275
- if (isToRemote) {
276
- if (newName) {
277
- await mkdirCmd(props.pid, props.sessionId, path)
278
- await delay(1000)
279
- }
280
- await unzipCmd(props.pid, props.sessionId, toPath, path)
281
- if (newName) {
282
- const mvFrom = resolve(path, name)
283
- const mvTo = resolve(targetPath, newName)
284
- await mvCmd(props.pid, props.sessionId, mvFrom, mvTo)
285
- }
286
- } else {
287
- if (newName) {
288
- await fs.mkdir(path)
289
- }
290
- await fs.unzipFile(toPath, path)
291
- if (newName) {
292
- const mvFrom = resolve(path, name)
293
- const mvTo = resolve(targetPath, newName)
294
- await fs.mv(mvFrom, mvTo)
295
- }
296
- }
297
- await rmCmd(props.pid, props.sessionId, !isToRemote ? fromPath : toPath)
298
- await fs.rmrf(!isToRemote ? toPath : fromPath)
299
- if (newName) {
300
- if (isToRemote) {
301
- await rmCmd(props.pid, props.sessionId, path)
302
- } else {
303
- await fs.rmrf(path)
304
- }
305
- }
306
- onEnd()
307
- }
308
-
309
- async function doTransfer () {
310
- const {
311
- fromPath,
312
- toPath,
313
- typeFrom,
314
- fromFile: {
315
- mode: fromMode
316
- },
317
- toFile = {}
318
- } = transfer
319
- const transferType = typeFrom === typeMap.local ? transferTypeMap.upload : transferTypeMap.download
320
- const isDown = transferType === transferTypeMap.download
321
- const localPath = isDown
322
- ? toPath
323
- : fromPath
324
- const remotePath = isDown
325
- ? fromPath
326
- : toPath
327
- const mode = toFile.mode || fromMode
328
- inst.current.transport = await props.sftp[transferType]({
329
- remotePath,
330
- localPath,
331
- options: { mode },
332
- onData,
333
- onError,
334
- onEnd
335
- })
336
- }
337
- function isTransferAction (action) {
338
- return action.includes('rename') || action === 'transfer'
339
- }
340
- async function initTransfer () {
341
- if (inst.current.started) {
342
- return
343
- }
344
- const {
345
- typeFrom,
346
- typeTo,
347
- fromFile: {
348
- isDirectory
349
- },
350
- action,
351
- expanded,
352
- zip,
353
- unzip,
354
- inited
355
- } = transfer
356
- const t = Date.now()
357
- update({
358
- startTime: t
359
- })
360
- inst.current.startTime = t
361
- inst.current.started = true
362
- if (unzip && inited) {
363
- unzipFile()
364
- } else if (zip && inited) {
365
- zipTransfer()
366
- } else if (typeFrom === typeTo) {
367
- return mvOrCp()
368
- } else if (isDirectory && expanded && isTransferAction(action)) {
369
- return mkdir()
370
- .then(onEnd)
371
- .catch(onError)
372
- } else if (!isDirectory) {
373
- doTransfer()
374
- } else if (expanded && isDirectory && !isTransferAction(action)) {
375
- cancel()
376
- }
377
- }
378
- function onError (e) {
379
- const up = {
380
- status: 'exception',
381
- error: e.message
382
- }
383
- onEnd(up)
384
- window.store.onError(e)
385
- }
386
- async function mkdir () {
387
- const {
388
- typeTo,
389
- toPath
390
- } = transfer
391
- if (typeTo === typeMap.local) {
392
- return fs.mkdir(toPath)
393
- .catch(onError)
394
- }
395
- return props.sftp.mkdir(toPath)
396
- .catch(onError)
397
- }
398
- useEffect(() => {
399
- initEvent()
400
- if (props.transfer.inited) {
401
- initTransfer()
402
- }
403
- return onDestroy
404
- }, [])
405
- useConditionalEffect(() => {
406
- initTransfer()
407
- }, initRef && initRef.prev !== initRef.curr && initRef.curr === true)
408
- useConditionalEffect(() => {
409
- initTransfer()
410
- }, initRefExpand && initRefExpand.prev !== initRefExpand.curr && initRef.curr === true)
411
- return null
412
- }