@electerm/electerm-react 1.72.48 → 1.80.2
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 +1 -1
- package/client/common/sftp.js +3 -1
- package/client/components/ai/ai-config.jsx +7 -1
- package/client/components/batch-op/batch-op.jsx +4 -5
- package/client/components/bg/css-overwrite.jsx +179 -0
- package/client/components/bg/shapes.js +501 -0
- package/client/components/bookmark-form/form-tabs.jsx +1 -0
- package/client/components/bookmark-form/local-form-ui.jsx +7 -1
- package/client/components/bookmark-form/render-bg.jsx +43 -0
- package/client/components/bookmark-form/serial-form-ui.jsx +7 -1
- package/client/components/bookmark-form/ssh-form-ui.jsx +14 -3
- package/client/components/bookmark-form/telnet-form-ui.jsx +7 -1
- package/client/components/bookmark-form/use-ui.jsx +68 -72
- package/client/components/common/ref.js +2 -0
- package/client/components/{sftp/confirm-modal-store.jsx → file-transfer/conflict-resolve.jsx} +86 -48
- package/client/components/file-transfer/transfer-queue.jsx +151 -0
- package/client/components/file-transfer/transfer.jsx +582 -0
- package/client/components/{sftp → file-transfer}/transports-action-store.jsx +35 -32
- package/client/components/{sftp → file-transfer}/transports-ui-store.jsx +2 -2
- package/client/components/main/main.jsx +25 -18
- package/client/components/main/wrapper.styl +16 -0
- package/client/components/profile/profile-list.jsx +1 -1
- package/client/components/quick-commands/qm.styl +4 -1
- package/client/components/quick-commands/quick-commands-list.jsx +16 -4
- package/client/components/setting-panel/list.jsx +1 -1
- package/client/components/setting-panel/setting-terminal.jsx +2 -1
- package/client/components/setting-panel/terminal-bg-config.jsx +15 -2
- package/client/components/sftp/file-info-modal.jsx +3 -0
- package/client/components/sftp/file-item.jsx +25 -23
- package/client/components/sftp/file-read.js +1 -27
- package/client/components/sftp/list-table-ui.jsx +2 -2
- package/client/components/sidebar/transfer-history-modal.jsx +1 -1
- package/client/components/sidebar/transfer-list-control.jsx +1 -1
- package/client/components/sidebar/transport-ui.jsx +16 -9
- package/client/components/terminal/terminal.jsx +23 -1
- package/client/components/text-editor/simple-editor.jsx +164 -0
- package/client/components/text-editor/text-editor-form.jsx +6 -9
- package/client/css/includes/box.styl +2 -2
- package/client/store/tab.js +5 -1
- package/client/store/transfer-list.js +10 -51
- package/package.json +1 -1
- package/client/components/main/css-overwrite.jsx +0 -91
- package/client/components/sftp/transfer-conflict-store.jsx +0 -284
- package/client/components/sftp/transport-action-store.jsx +0 -422
- package/client/components/sftp/zip.js +0 -42
- /package/client/components/{main → bg}/custom-css.jsx +0 -0
- /package/client/components/{sftp → file-transfer}/transfer-speed-format.js +0 -0
- /package/client/components/{sftp → file-transfer}/transfer.styl +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react'
|
|
2
|
+
import { Input, Button, Flex } from 'antd'
|
|
3
|
+
import {
|
|
4
|
+
ArrowUpOutlined,
|
|
5
|
+
ArrowDownOutlined,
|
|
6
|
+
SearchOutlined,
|
|
7
|
+
CopyOutlined
|
|
8
|
+
} from '@ant-design/icons'
|
|
9
|
+
import { copy } from '../../common/clipboard'
|
|
10
|
+
|
|
11
|
+
export default function SimpleEditor (props) {
|
|
12
|
+
const [searchKeyword, setSearchKeyword] = useState('')
|
|
13
|
+
const [occurrences, setOccurrences] = useState([])
|
|
14
|
+
const [currentMatch, setCurrentMatch] = useState(-1)
|
|
15
|
+
const [isNavigating, setIsNavigating] = useState(false)
|
|
16
|
+
const editorRef = useRef(null)
|
|
17
|
+
|
|
18
|
+
// When currentMatch changes, highlight the match in textarea
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (currentMatch >= 0 && occurrences.length > 0) {
|
|
21
|
+
const match = occurrences[currentMatch]
|
|
22
|
+
if (editorRef.current) {
|
|
23
|
+
// Set selection range to select the matched text
|
|
24
|
+
editorRef.current.resizableTextArea.textArea.setSelectionRange(match.start, match.end)
|
|
25
|
+
|
|
26
|
+
// Only focus the textarea when explicitly navigating between matches
|
|
27
|
+
if (isNavigating) {
|
|
28
|
+
editorRef.current.resizableTextArea.textArea.focus()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Scroll to the selection position
|
|
32
|
+
const textarea = editorRef.current.resizableTextArea.textArea
|
|
33
|
+
const textBeforeSelection = props.value.substring(0, match.start)
|
|
34
|
+
const lineBreaks = textBeforeSelection.split('\n').length - 1
|
|
35
|
+
|
|
36
|
+
// Estimate the scroll position
|
|
37
|
+
const lineHeight = 20 // Approximate line height in pixels
|
|
38
|
+
const scrollPosition = lineHeight * lineBreaks
|
|
39
|
+
|
|
40
|
+
textarea.scrollTop = Math.max(0, scrollPosition - textarea.clientHeight / 2)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Reset navigating flag after using it
|
|
44
|
+
setIsNavigating(false)
|
|
45
|
+
}, [currentMatch, occurrences])
|
|
46
|
+
// Copy the editor content to clipboard
|
|
47
|
+
const copyEditorContent = () => {
|
|
48
|
+
copy(props.value || '')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Find all matches of the search keyword in text
|
|
52
|
+
const findMatches = () => {
|
|
53
|
+
if (!searchKeyword) {
|
|
54
|
+
setOccurrences([])
|
|
55
|
+
setCurrentMatch(-1)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const matches = []
|
|
60
|
+
const text = props.value || ''
|
|
61
|
+
const regex = new RegExp(searchKeyword, 'gi')
|
|
62
|
+
let match
|
|
63
|
+
|
|
64
|
+
while ((match = regex.exec(text)) !== null) {
|
|
65
|
+
matches.push({
|
|
66
|
+
start: match.index,
|
|
67
|
+
end: match.index + searchKeyword.length
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
setOccurrences(matches)
|
|
71
|
+
setCurrentMatch(matches.length ? 0 : -1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle search action when user presses enter or clicks the search button
|
|
75
|
+
const handleSearch = (e) => {
|
|
76
|
+
e.stopPropagation()
|
|
77
|
+
e.preventDefault()
|
|
78
|
+
findMatches()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Navigate to next match
|
|
82
|
+
const goToNextMatch = () => {
|
|
83
|
+
setIsNavigating(true)
|
|
84
|
+
if (currentMatch < occurrences.length - 1) {
|
|
85
|
+
setCurrentMatch(currentMatch + 1)
|
|
86
|
+
} else {
|
|
87
|
+
setCurrentMatch(0) // Loop back to first match
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Navigate to previous match
|
|
92
|
+
const goToPrevMatch = () => {
|
|
93
|
+
setIsNavigating(true)
|
|
94
|
+
if (currentMatch > 0) {
|
|
95
|
+
setCurrentMatch(currentMatch - 1)
|
|
96
|
+
} else {
|
|
97
|
+
setCurrentMatch(occurrences.length - 1) // Loop to last match
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Render navigation buttons for search results
|
|
102
|
+
const renderNavigationButtons = () => {
|
|
103
|
+
if (occurrences.length === 0) {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
return (
|
|
107
|
+
<>
|
|
108
|
+
<Button onClick={goToPrevMatch}>
|
|
109
|
+
<ArrowUpOutlined />
|
|
110
|
+
</Button>
|
|
111
|
+
<Button onClick={goToNextMatch}>
|
|
112
|
+
<ArrowDownOutlined />
|
|
113
|
+
</Button>
|
|
114
|
+
</>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Render search results counter
|
|
119
|
+
const renderSearchCounter = () => {
|
|
120
|
+
return occurrences.length
|
|
121
|
+
? `${currentMatch + 1}/${occurrences.length}`
|
|
122
|
+
: '0/0'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function renderAfter () {
|
|
126
|
+
return (
|
|
127
|
+
<>
|
|
128
|
+
<b className='pd1x'>{renderSearchCounter()}</b>
|
|
129
|
+
{renderNavigationButtons()}
|
|
130
|
+
</>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div>
|
|
136
|
+
<Flex className='mg1b' justify='space-between'>
|
|
137
|
+
<Input.Search
|
|
138
|
+
value={searchKeyword}
|
|
139
|
+
onChange={e => setSearchKeyword(e.target.value)}
|
|
140
|
+
placeholder='Search in text...'
|
|
141
|
+
allowClear
|
|
142
|
+
enterButton={<SearchOutlined />}
|
|
143
|
+
onSearch={handleSearch}
|
|
144
|
+
onPressEnter={handleSearch}
|
|
145
|
+
addonAfter={renderAfter()}
|
|
146
|
+
style={{ width: 'auto' }}
|
|
147
|
+
/>
|
|
148
|
+
<Button
|
|
149
|
+
onClick={copyEditorContent}
|
|
150
|
+
className='mg3l'
|
|
151
|
+
>
|
|
152
|
+
<CopyOutlined />
|
|
153
|
+
</Button>
|
|
154
|
+
</Flex>
|
|
155
|
+
<Input.TextArea
|
|
156
|
+
ref={editorRef}
|
|
157
|
+
value={props.value}
|
|
158
|
+
onChange={props.onChange}
|
|
159
|
+
rows={20}
|
|
160
|
+
/>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
)
|
|
164
|
+
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useEffect } from 'react'
|
|
6
|
-
import {
|
|
6
|
+
import { Form, Button } from 'antd'
|
|
7
|
+
import SimpleEditor from './simple-editor'
|
|
7
8
|
|
|
8
9
|
const FormItem = Form.Item
|
|
9
10
|
const e = window.translate
|
|
@@ -23,9 +24,9 @@ export default function TextEditorForm (props) {
|
|
|
23
24
|
props.submit(res)
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function onPressEnter (e) {
|
|
27
|
-
|
|
28
|
-
}
|
|
27
|
+
// function onPressEnter (e) {
|
|
28
|
+
// e.stopPropagation()
|
|
29
|
+
// }
|
|
29
30
|
|
|
30
31
|
function reset () {
|
|
31
32
|
form.resetFields()
|
|
@@ -58,11 +59,7 @@ export default function TextEditorForm (props) {
|
|
|
58
59
|
<FormItem
|
|
59
60
|
name='text'
|
|
60
61
|
>
|
|
61
|
-
<
|
|
62
|
-
rows={20}
|
|
63
|
-
onPressEnter={onPressEnter}
|
|
64
|
-
>{text}
|
|
65
|
-
</Input.TextArea>
|
|
62
|
+
<SimpleEditor />
|
|
66
63
|
</FormItem>
|
|
67
64
|
<div className='pd1t pd2b'>
|
|
68
65
|
<Button
|
package/client/store/tab.js
CHANGED
|
@@ -467,7 +467,11 @@ export default Store => {
|
|
|
467
467
|
'status',
|
|
468
468
|
'pane',
|
|
469
469
|
'batch',
|
|
470
|
-
'tabCount'
|
|
470
|
+
'tabCount',
|
|
471
|
+
'sftpCreated',
|
|
472
|
+
'sshSftpSplitView',
|
|
473
|
+
'sshTunnelResults',
|
|
474
|
+
'displayRaw'
|
|
471
475
|
]
|
|
472
476
|
const { history } = store
|
|
473
477
|
const index = history.findIndex(d => {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* file transfer list related functions
|
|
3
3
|
*/
|
|
4
|
+
|
|
5
|
+
import uid from '../common/uid'
|
|
6
|
+
|
|
4
7
|
const { assign } = Object
|
|
5
8
|
|
|
6
9
|
export default Store => {
|
|
@@ -18,17 +21,13 @@ export default Store => {
|
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
Store.prototype.addTransferList = function (items) {
|
|
24
|
+
// console.log('addTransferList', JSON.stringify(items, null, 2))
|
|
21
25
|
const { fileTransfers } = window.store
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const index = fileTransfers.findIndex(t => t.id === itemId)
|
|
28
|
-
if (index < 0) {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
fileTransfers[index].pausing = !fileTransfers[index].pausing
|
|
26
|
+
const transferBatch = uid()
|
|
27
|
+
fileTransfers.push(...items.map(t => {
|
|
28
|
+
t.transferBatch = transferBatch
|
|
29
|
+
return t
|
|
30
|
+
}))
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
Store.prototype.pauseAll = function () {
|
|
@@ -50,46 +49,6 @@ export default Store => {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
Store.prototype.cancelAll = function () {
|
|
53
|
-
|
|
54
|
-
const len = fileTransfers.length
|
|
55
|
-
for (let i = len - 1; i >= 0; i--) {
|
|
56
|
-
fileTransfers[i].cancel = true
|
|
57
|
-
fileTransfers.splice(i, 1)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
Store.prototype.cancelTransfer = function (itemId) {
|
|
62
|
-
const { fileTransfers } = window.store
|
|
63
|
-
const index = fileTransfers.findIndex(t => t.id === itemId)
|
|
64
|
-
if (index < 0) {
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
fileTransfers[index].cancel = true
|
|
68
|
-
fileTransfers.splice(index, 1)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
Store.prototype.skipAllTransfersSinceIndex = function (index) {
|
|
72
|
-
window.store.fileTransfers.splice(index)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
Store.prototype.updateTransfersFromIndex = function (index, update) {
|
|
76
|
-
const { fileTransfers } = window.store
|
|
77
|
-
if (index < 0 || index >= fileTransfers.length) {
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
const len = fileTransfers.length
|
|
81
|
-
for (let i = index; i < len; i++) {
|
|
82
|
-
assign(fileTransfers[i], update)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Add a new method to find index by ID and then update
|
|
87
|
-
Store.prototype.updateTransfersFromId = function (id, update) {
|
|
88
|
-
const { fileTransfers } = window.store
|
|
89
|
-
const index = fileTransfers.findIndex(t => t.id === id)
|
|
90
|
-
if (index < 0) {
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
window.store.updateTransfersFromIndex(index, update)
|
|
52
|
+
window.store.fileTransfers = []
|
|
94
53
|
}
|
|
95
54
|
}
|
package/package.json
CHANGED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* btns
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { PureComponent } from 'react'
|
|
6
|
-
import fs from '../../common/fs'
|
|
7
|
-
import { noTerminalBgValue } from '../../common/constants'
|
|
8
|
-
|
|
9
|
-
export default class CssOverwrite extends PureComponent {
|
|
10
|
-
// componentDidMount () {
|
|
11
|
-
// setTimeout(this.writeCss, 3000)
|
|
12
|
-
// }
|
|
13
|
-
|
|
14
|
-
componentDidUpdate (prevProps) {
|
|
15
|
-
Object.keys(this.props).some(key => {
|
|
16
|
-
if (key.startsWith('terminalBackground') && prevProps[key] !== this.props[key]) {
|
|
17
|
-
this.updateCss()
|
|
18
|
-
return true
|
|
19
|
-
}
|
|
20
|
-
return false
|
|
21
|
-
})
|
|
22
|
-
if (!prevProps.wsInited && this.props.wsInited) {
|
|
23
|
-
this.writeCss()
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
createStyle = async () => {
|
|
28
|
-
const { terminalBackgroundImagePath } = this.props
|
|
29
|
-
let content = ''
|
|
30
|
-
let st = ''
|
|
31
|
-
const isWebImg = /^https?:\/\//.test(terminalBackgroundImagePath)
|
|
32
|
-
if (noTerminalBgValue === terminalBackgroundImagePath) {
|
|
33
|
-
st = 'none'
|
|
34
|
-
} else if (terminalBackgroundImagePath && !isWebImg) {
|
|
35
|
-
content = await fs.readFileAsBase64(terminalBackgroundImagePath)
|
|
36
|
-
.catch(log.error)
|
|
37
|
-
if (content) {
|
|
38
|
-
st = `url(data:image;base64,${content}) !important`
|
|
39
|
-
}
|
|
40
|
-
} else if (terminalBackgroundImagePath && isWebImg) {
|
|
41
|
-
st = `url(${terminalBackgroundImagePath}) !important`
|
|
42
|
-
}
|
|
43
|
-
if (!st) {
|
|
44
|
-
return `#container .session-batch-active .xterm-screen::before {
|
|
45
|
-
background-image: url("./images/electerm-watermark.png");
|
|
46
|
-
}`
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const styles = [
|
|
50
|
-
`background-image: ${st}`,
|
|
51
|
-
'background-position: center'
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
if (st !== 'none') {
|
|
55
|
-
styles.push(`filter: blur(${
|
|
56
|
-
this.props.terminalBackgroundFilterBlur
|
|
57
|
-
}px) opacity(${
|
|
58
|
-
+this.props.terminalBackgroundFilterOpacity
|
|
59
|
-
}) brightness(${
|
|
60
|
-
+this.props.terminalBackgroundFilterBrightness
|
|
61
|
-
}) contrast(${
|
|
62
|
-
+this.props.terminalBackgroundFilterContrast
|
|
63
|
-
}) grayscale(${
|
|
64
|
-
+this.props.terminalBackgroundFilterGrayscale
|
|
65
|
-
})`)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return `#container .session-batch-active .xterm-screen::before {
|
|
69
|
-
${styles.join(';')}
|
|
70
|
-
}`
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
writeCss = async () => {
|
|
74
|
-
const style = document.createElement('style')
|
|
75
|
-
style.type = 'text/css'
|
|
76
|
-
style.innerHTML = await this.createStyle()
|
|
77
|
-
style.id = 'css-overwrite'
|
|
78
|
-
document.getElementsByTagName('head')[0].appendChild(style)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
updateCss = async () => {
|
|
82
|
-
const style = document.getElementById('css-overwrite')
|
|
83
|
-
if (style) {
|
|
84
|
-
style.innerHTML = await this.createStyle()
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
render () {
|
|
89
|
-
return null
|
|
90
|
-
}
|
|
91
|
-
}
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pass transfer list from props
|
|
3
|
-
* when list changes, do transfer and other op
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { PureComponent } from 'react'
|
|
7
|
-
import { typeMap } from '../../common/constants'
|
|
8
|
-
import {
|
|
9
|
-
getLocalFileInfo,
|
|
10
|
-
getRemoteFileInfo,
|
|
11
|
-
getFolderFromFilePath,
|
|
12
|
-
getFileExt,
|
|
13
|
-
checkFolderSize
|
|
14
|
-
} from './file-read'
|
|
15
|
-
import { refsStatic, refs } from '../common/ref'
|
|
16
|
-
import generate from '../../common/uid'
|
|
17
|
-
import resolve from '../../common/resolve'
|
|
18
|
-
import deepCopy from 'json-deep-copy'
|
|
19
|
-
|
|
20
|
-
const { assign } = Object
|
|
21
|
-
|
|
22
|
-
export default class TransferConflictStore extends PureComponent {
|
|
23
|
-
state = {
|
|
24
|
-
currentId: ''
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
componentDidMount () {
|
|
28
|
-
this.id = 'transfer-conflict'
|
|
29
|
-
refsStatic.add(this.id, this)
|
|
30
|
-
this.watchFile()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
componentDidUpdate (prevProps) {
|
|
34
|
-
if (
|
|
35
|
-
prevProps.fileTransferChanged !== this.props.fileTransferChanged
|
|
36
|
-
) {
|
|
37
|
-
this.watchFile()
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
localCheckExist = (path) => {
|
|
42
|
-
return getLocalFileInfo(path).catch(console.log)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
remoteCheckExist = (path, sessionId) => {
|
|
46
|
-
const sftp = refs.get('sftp-' + sessionId).sftp
|
|
47
|
-
return getRemoteFileInfo(sftp, path)
|
|
48
|
-
.then(r => r)
|
|
49
|
-
.catch(() => false)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
checkExist = (type, path, sessionId) => {
|
|
53
|
-
return this[type + 'CheckExist'](path, sessionId)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
rename = (tr, action, _renameId) => {
|
|
57
|
-
const isRemote = tr.typeTo === typeMap.remote
|
|
58
|
-
const { path, name } = getFolderFromFilePath(tr.toPath, isRemote)
|
|
59
|
-
const { base, ext } = getFileExt(name)
|
|
60
|
-
const renameId = _renameId || generate()
|
|
61
|
-
const newName = ext
|
|
62
|
-
? `${base}(rename-${renameId}).${ext}`
|
|
63
|
-
: `${base}(rename-${renameId})`
|
|
64
|
-
assign(tr, {
|
|
65
|
-
renameId,
|
|
66
|
-
newName,
|
|
67
|
-
oldName: base,
|
|
68
|
-
toPath: resolve(path, newName)
|
|
69
|
-
})
|
|
70
|
-
if (action) {
|
|
71
|
-
tr.action = action
|
|
72
|
-
}
|
|
73
|
-
return tr
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
updateTransferAction = (data) => {
|
|
77
|
-
const {
|
|
78
|
-
id,
|
|
79
|
-
action,
|
|
80
|
-
transfer
|
|
81
|
-
} = data
|
|
82
|
-
const {
|
|
83
|
-
fromFile
|
|
84
|
-
} = transfer
|
|
85
|
-
this.clear()
|
|
86
|
-
|
|
87
|
-
const { store } = window
|
|
88
|
-
const { fileTransfers } = store
|
|
89
|
-
const index = fileTransfers.findIndex(d => d.id === id)
|
|
90
|
-
if (index < 0) {
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
const tr = fileTransfers[index]
|
|
94
|
-
tr.fromFile = deepCopy(fromFile)
|
|
95
|
-
tr.action = action
|
|
96
|
-
tr.r = Math.random()
|
|
97
|
-
if (action === 'skip') {
|
|
98
|
-
return fileTransfers.splice(index, 1)
|
|
99
|
-
} else if (action === 'cancel') {
|
|
100
|
-
return store.skipAllTransfersSinceIndex(index)
|
|
101
|
-
}
|
|
102
|
-
if (action.includes('All')) {
|
|
103
|
-
return store.updateTransfersFromIndex(index, {
|
|
104
|
-
action: action.replace('All', '')
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
if (action.includes('rename')) {
|
|
108
|
-
return this.rename(tr)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
tagTransferError = (id, errorMsg) => {
|
|
113
|
-
this.clear()
|
|
114
|
-
const { store } = window
|
|
115
|
-
const { fileTransfers } = store
|
|
116
|
-
const index = fileTransfers.findIndex(d => d.id === id)
|
|
117
|
-
if (index < 0) {
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const [tr] = fileTransfers.splice(index, 1)
|
|
122
|
-
assign(tr, {
|
|
123
|
-
host: tr.host,
|
|
124
|
-
error: errorMsg,
|
|
125
|
-
finishTime: Date.now()
|
|
126
|
-
})
|
|
127
|
-
store.addTransferHistory(tr)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
setConflict (tr) {
|
|
131
|
-
if (this.props.transferToConfirm.id) {
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
window.store.transferToConfirm = tr
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
onDecision = (data) => {
|
|
138
|
-
if (
|
|
139
|
-
data.id === this.currentId
|
|
140
|
-
) {
|
|
141
|
-
this.currentId = ''
|
|
142
|
-
this.updateTransferAction(data)
|
|
143
|
-
this.onConfirm = false
|
|
144
|
-
window.removeEventListener('message', this.onDecision)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
updateData = () => {
|
|
149
|
-
const {
|
|
150
|
-
store
|
|
151
|
-
} = window
|
|
152
|
-
const {
|
|
153
|
-
fileTransfers
|
|
154
|
-
} = store
|
|
155
|
-
if (fileTransfers.length > 0) {
|
|
156
|
-
fileTransfers[0].r = Math.random()
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
setCanTransfer = (fromFile, tr) => {
|
|
161
|
-
this.clear()
|
|
162
|
-
const {
|
|
163
|
-
store
|
|
164
|
-
} = window
|
|
165
|
-
const {
|
|
166
|
-
fileTransfers
|
|
167
|
-
} = store
|
|
168
|
-
const index = fileTransfers.findIndex(t => {
|
|
169
|
-
return t.id === tr.id
|
|
170
|
-
})
|
|
171
|
-
if (index < 0) {
|
|
172
|
-
setTimeout(this.updateData, 0)
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
const up = {
|
|
176
|
-
action: 'transfer',
|
|
177
|
-
fromFile
|
|
178
|
-
}
|
|
179
|
-
assign(fileTransfers[index], up)
|
|
180
|
-
// may have issue
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
clear = () => {
|
|
184
|
-
this.currentId = ''
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
watchFile = async () => {
|
|
188
|
-
const { store } = window
|
|
189
|
-
const {
|
|
190
|
-
fileTransfers
|
|
191
|
-
} = store
|
|
192
|
-
if (!fileTransfers.length) {
|
|
193
|
-
return this.clear()
|
|
194
|
-
}
|
|
195
|
-
const tr = fileTransfers
|
|
196
|
-
.find(t => {
|
|
197
|
-
return (
|
|
198
|
-
!t.action ||
|
|
199
|
-
!t.fromFile ||
|
|
200
|
-
t.fromFile.isDirectory
|
|
201
|
-
)
|
|
202
|
-
})
|
|
203
|
-
if (!tr) {
|
|
204
|
-
this.onConfirm = false
|
|
205
|
-
return this.clear()
|
|
206
|
-
}
|
|
207
|
-
if (this.currentId) {
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
this.currentId = tr.id
|
|
211
|
-
const {
|
|
212
|
-
typeFrom,
|
|
213
|
-
typeTo,
|
|
214
|
-
fromPath,
|
|
215
|
-
toPath,
|
|
216
|
-
id,
|
|
217
|
-
action,
|
|
218
|
-
renameId,
|
|
219
|
-
parentId,
|
|
220
|
-
skipConfirm,
|
|
221
|
-
sessionId
|
|
222
|
-
} = tr
|
|
223
|
-
const fromFile = tr.fromFile
|
|
224
|
-
? tr.fromFile
|
|
225
|
-
: await this.checkExist(typeFrom, fromPath, sessionId)
|
|
226
|
-
if (!fromFile) {
|
|
227
|
-
return this.tagTransferError(id, 'file not exist')
|
|
228
|
-
}
|
|
229
|
-
let toFile = false
|
|
230
|
-
if (renameId || parentId) {
|
|
231
|
-
toFile = false
|
|
232
|
-
} else if (fromPath === toPath && typeFrom === typeTo) {
|
|
233
|
-
toFile = true
|
|
234
|
-
} else {
|
|
235
|
-
toFile = await this.checkExist(typeTo, toPath, sessionId)
|
|
236
|
-
}
|
|
237
|
-
if (fromFile.isDirectory && typeFrom !== typeTo) {
|
|
238
|
-
const props = {
|
|
239
|
-
sftp: refs.get('sftp-' + sessionId).sftp
|
|
240
|
-
}
|
|
241
|
-
const skip = await checkFolderSize(props, fromFile)
|
|
242
|
-
.then(d => d && typeFrom !== typeTo)
|
|
243
|
-
if (!skip) {
|
|
244
|
-
return this.tagTransferError(id, 'folder too big or too many files in folder')
|
|
245
|
-
}
|
|
246
|
-
tr.zip = true
|
|
247
|
-
tr.skipExpand = true
|
|
248
|
-
}
|
|
249
|
-
if (fromPath === toPath && typeFrom === typeTo) {
|
|
250
|
-
assign(tr, {
|
|
251
|
-
operation: 'cp',
|
|
252
|
-
fromFile
|
|
253
|
-
})
|
|
254
|
-
return this.updateTransferAction({
|
|
255
|
-
id,
|
|
256
|
-
action: 'rename',
|
|
257
|
-
transfer: tr
|
|
258
|
-
})
|
|
259
|
-
} else if (toFile && !action && !skipConfirm) {
|
|
260
|
-
if (!this.onConfirm) {
|
|
261
|
-
this.onConfirm = true
|
|
262
|
-
assign(tr, {
|
|
263
|
-
fromFile,
|
|
264
|
-
toFile
|
|
265
|
-
})
|
|
266
|
-
return this.setConflict(tr)
|
|
267
|
-
}
|
|
268
|
-
} else if (toFile && !tr.fromFile && action) {
|
|
269
|
-
assign(tr, {
|
|
270
|
-
fromFile
|
|
271
|
-
})
|
|
272
|
-
return this.updateTransferAction({
|
|
273
|
-
id,
|
|
274
|
-
action,
|
|
275
|
-
transfer: tr
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
this.setCanTransfer(fromFile, tr)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
render () {
|
|
282
|
-
return null
|
|
283
|
-
}
|
|
284
|
-
}
|