@electerm/electerm-react 3.0.18 → 3.1.16
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 +4 -4
- package/client/common/db.js +20 -10
- package/client/components/file-transfer/conflict-resolve.jsx +38 -4
- package/client/components/file-transfer/transfer-queue.jsx +10 -4
- package/client/components/file-transfer/transfer.jsx +7 -4
- package/client/components/file-transfer/transports-action-store.jsx +14 -2
- package/client/components/footer/cmd-history.jsx +2 -4
- package/client/components/quick-commands/qm.styl +8 -0
- package/client/components/quick-commands/quick-command-item.jsx +4 -0
- package/client/components/quick-commands/quick-commands-box.jsx +10 -0
- package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -1
- package/client/components/quick-commands/quick-commands-list-form.jsx +59 -4
- package/client/components/quick-commands/quick-commands-list.jsx +33 -3
- package/client/components/setting-panel/list.styl +5 -1
- package/client/components/setting-sync/server-data-status.jsx +2 -1
- package/client/components/setting-sync/setting-sync-form.jsx +93 -15
- package/client/components/setting-sync/setting-sync.jsx +5 -1
- package/client/components/sidebar/transfer-history-modal.jsx +2 -2
- package/client/components/tabs/tabs.styl +1 -0
- package/client/components/tabs/window-control.jsx +2 -0
- package/client/components/terminal/terminal-command-dropdown.jsx +1 -1
- package/client/components/terminal/terminal.jsx +14 -1
- package/client/components/terminal/transfer-client-base.js +38 -17
- package/client/components/terminal-info/network.jsx +0 -1
- package/client/components/tree-list/tree-list-item.jsx +5 -0
- package/client/components/tree-list/tree-list.jsx +17 -4
- package/client/components/tree-list/tree-list.styl +1 -1
- package/client/store/common.js +15 -15
- package/client/store/init-state.js +6 -31
- package/client/store/mcp-handler.js +315 -129
- package/client/store/store.js +1 -1
- package/client/store/sync.js +129 -3
- package/client/store/transfer-list.js +3 -2
- package/client/store/watch.js +1 -25
- package/package.json +1 -1
|
@@ -190,13 +190,15 @@ export const syncTypes = buildConst([
|
|
|
190
190
|
'github',
|
|
191
191
|
'gitee',
|
|
192
192
|
'custom',
|
|
193
|
-
'cloud'
|
|
193
|
+
'cloud',
|
|
194
|
+
'webdav'
|
|
194
195
|
])
|
|
195
196
|
export const syncTokenCreateUrls = {
|
|
196
197
|
gitee: 'https://gitee.com/github-zxdong262/electerm/wikis/Create%20personal%20access%20token?sort_id=3028409',
|
|
197
198
|
github: 'https://github.com/electerm/electerm/wiki/Create-personal-access-token',
|
|
198
199
|
custom: 'https://github.com/electerm/electerm/wiki/Custom-sync-server',
|
|
199
|
-
cloud: 'https://electerm-cloud.html5beta.com'
|
|
200
|
+
cloud: 'https://electerm-cloud.html5beta.com',
|
|
201
|
+
webdav: 'https://github.com/electerm/electerm/wiki/WebDAV-sync'
|
|
200
202
|
}
|
|
201
203
|
export const settingSyncId = 'setting-sync'
|
|
202
204
|
export const settingTerminalId = 'setting-terminal'
|
|
@@ -350,6 +352,4 @@ export const terminalTypes = [
|
|
|
350
352
|
export const sshConfigLoadKey = 'ssh-config-loaded'
|
|
351
353
|
export const sshConfigKey = 'ignore-ssh-config'
|
|
352
354
|
export const connectionHoppingWarnKey = 'connectionHoppingWarnned'
|
|
353
|
-
export const aiChatHistoryKey = 'ai-chat-history'
|
|
354
355
|
export const syncServerDataKey = 'sync-server-data'
|
|
355
|
-
export const cmdHistoryKey = 'cmd-history'
|
package/client/common/db.js
CHANGED
|
@@ -23,17 +23,27 @@ const dbAction = (...args) => {
|
|
|
23
23
|
/**
|
|
24
24
|
* standalone db names
|
|
25
25
|
*/
|
|
26
|
-
export const dbNames =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
export const dbNames = [
|
|
27
|
+
...without(
|
|
28
|
+
Object.keys(settingMap),
|
|
29
|
+
settingMap.setting,
|
|
30
|
+
settingMap.widgets
|
|
31
|
+
),
|
|
32
|
+
'history',
|
|
33
|
+
'terminalCommandHistory',
|
|
34
|
+
'aiChatHistory'
|
|
35
|
+
]
|
|
31
36
|
|
|
32
|
-
export const dbNamesForWatch =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
export const dbNamesForWatch = [
|
|
38
|
+
...without(
|
|
39
|
+
Object.keys(settingMap),
|
|
40
|
+
settingMap.setting,
|
|
41
|
+
settingMap.widgets
|
|
42
|
+
),
|
|
43
|
+
'history',
|
|
44
|
+
'terminalCommandHistory',
|
|
45
|
+
'aiChatHistory'
|
|
46
|
+
]
|
|
37
47
|
|
|
38
48
|
/**
|
|
39
49
|
* db insert
|
|
@@ -31,25 +31,50 @@ export default class ConfirmModalStore extends Component {
|
|
|
31
31
|
transferToConfirm: null
|
|
32
32
|
}
|
|
33
33
|
this.queue = []
|
|
34
|
+
this.queuedTransferIds = new Set()
|
|
35
|
+
this.activeTransferId = null
|
|
34
36
|
this.id = 'transfer-conflict'
|
|
35
37
|
refsStatic.add(this.id, this)
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
addConflict = (transfer) => {
|
|
41
|
+
const transferId = transfer?.id
|
|
42
|
+
if (!transferId) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
if (this.activeTransferId === transferId || this.queuedTransferIds.has(transferId)) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
const globalPolicy = window._transferConflictPolicy
|
|
49
|
+
if (globalPolicy && Object.values(fileActions).includes(globalPolicy)) {
|
|
50
|
+
const { id, transferBatch } = transfer
|
|
51
|
+
const trid = `tr-${transferBatch}-${id}`
|
|
52
|
+
const currentTransfer = refsTransfers.get(trid)
|
|
53
|
+
currentTransfer?.onDecision(globalPolicy)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
39
56
|
this.queue.push(transfer)
|
|
40
|
-
|
|
57
|
+
this.queuedTransferIds.add(transferId)
|
|
58
|
+
if (!this.activeTransferId) {
|
|
41
59
|
this.showNext()
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
showNext = () => {
|
|
46
64
|
const next = this.queue.shift()
|
|
65
|
+
if (next?.id) {
|
|
66
|
+
this.queuedTransferIds.delete(next.id)
|
|
67
|
+
}
|
|
68
|
+
this.activeTransferId = next?.id || null
|
|
47
69
|
this.setState({
|
|
48
70
|
transferToConfirm: next
|
|
49
71
|
})
|
|
50
72
|
}
|
|
51
73
|
|
|
52
74
|
act = (action) => {
|
|
75
|
+
if (!this.state.transferToConfirm) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
53
78
|
const { id, transferBatch } = this.state.transferToConfirm
|
|
54
79
|
const toAll = action.includes('All')
|
|
55
80
|
const policy = toAll ? action.replace('All', '') : action
|
|
@@ -60,10 +85,17 @@ export default class ConfirmModalStore extends Component {
|
|
|
60
85
|
if (doFilter) {
|
|
61
86
|
// Update all existing transfers with same batch ID in DOM
|
|
62
87
|
const prefix = `tr-${transferBatch}-`
|
|
88
|
+
const pendingConflictIds = new Set([
|
|
89
|
+
id,
|
|
90
|
+
...this.queue
|
|
91
|
+
.filter(d => d.transferBatch === transferBatch)
|
|
92
|
+
.map(d => d.id)
|
|
93
|
+
])
|
|
63
94
|
for (const [key, r] of window.refsTransfers.entries()) {
|
|
64
95
|
if (key.startsWith(prefix)) {
|
|
65
|
-
|
|
66
|
-
|
|
96
|
+
r.resolvePolicy = policy
|
|
97
|
+
const transferId = r.props.transfer?.id
|
|
98
|
+
if (key !== trid && pendingConflictIds.has(transferId)) {
|
|
67
99
|
r.onDecision(policy)
|
|
68
100
|
}
|
|
69
101
|
}
|
|
@@ -72,9 +104,11 @@ export default class ConfirmModalStore extends Component {
|
|
|
72
104
|
}
|
|
73
105
|
|
|
74
106
|
// Resolve current conflict
|
|
75
|
-
refsTransfers.get(trid)
|
|
107
|
+
const currentTransfer = refsTransfers.get(trid)
|
|
108
|
+
currentTransfer?.onDecision(policy)
|
|
76
109
|
|
|
77
110
|
// Move to the next item
|
|
111
|
+
this.activeTransferId = null
|
|
78
112
|
this.setState({
|
|
79
113
|
transferToConfirm: null
|
|
80
114
|
}, this.showNext)
|
|
@@ -41,7 +41,12 @@ export default class Queue extends Component {
|
|
|
41
41
|
return new Promise((resolve, reject) => {
|
|
42
42
|
const { fileTransfers } = window.store
|
|
43
43
|
const [id, updateObj] = args
|
|
44
|
+
let completed = false
|
|
44
45
|
const end = () => {
|
|
46
|
+
if (completed) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
completed = true
|
|
45
50
|
this.currentRun && this.currentRun.stop()
|
|
46
51
|
resolve()
|
|
47
52
|
}
|
|
@@ -99,10 +104,11 @@ export default class Queue extends Component {
|
|
|
99
104
|
})()
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
// Progress and delete updates are synchronous store mutations.
|
|
108
|
+
// Resolve them here so the queue cannot stall waiting for another reaction.
|
|
109
|
+
if (!isTransferInit) {
|
|
110
|
+
checkCompletion()
|
|
111
|
+
}
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
function checkCompletion () {
|
|
@@ -77,11 +77,11 @@ export default class TransportAction extends Component {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
localCheckExist = (path) => {
|
|
80
|
-
return getLocalFileInfo(path)
|
|
80
|
+
return getLocalFileInfo(path)
|
|
81
|
+
.catch(() => null)
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
remoteCheckExist = (path, tabId) => {
|
|
84
|
-
// return true
|
|
85
85
|
const sftp = refs.get('sftp-' + tabId)?.sftp
|
|
86
86
|
if (!sftp) {
|
|
87
87
|
console.log('remoteCheckExist error', 'sftp not exist')
|
|
@@ -373,10 +373,12 @@ export default class TransportAction extends Component {
|
|
|
373
373
|
toFile
|
|
374
374
|
})
|
|
375
375
|
if (transfer.resolvePolicy) {
|
|
376
|
-
|
|
376
|
+
this.onDecision(transfer.resolvePolicy)
|
|
377
|
+
return true
|
|
377
378
|
}
|
|
378
379
|
if (this.resolvePolicy) {
|
|
379
|
-
|
|
380
|
+
this.onDecision(this.resolvePolicy)
|
|
381
|
+
return true
|
|
380
382
|
}
|
|
381
383
|
const transferWithToFile = {
|
|
382
384
|
...copy(transfer),
|
|
@@ -386,6 +388,7 @@ export default class TransportAction extends Component {
|
|
|
386
388
|
refsStatic.get('transfer-conflict')?.addConflict(transferWithToFile)
|
|
387
389
|
return true
|
|
388
390
|
}
|
|
391
|
+
return false
|
|
389
392
|
}
|
|
390
393
|
|
|
391
394
|
onDecision = (policy) => {
|
|
@@ -12,6 +12,11 @@ import { refsStatic } from '../common/ref'
|
|
|
12
12
|
window.initingFtpTabIds = new Set()
|
|
13
13
|
|
|
14
14
|
export default class TransportsActionStore extends Component {
|
|
15
|
+
constructor (props) {
|
|
16
|
+
super(props)
|
|
17
|
+
this.pendingInitIds = new Set()
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
componentDidMount () {
|
|
16
21
|
this.control()
|
|
17
22
|
}
|
|
@@ -29,6 +34,12 @@ export default class TransportsActionStore extends Component {
|
|
|
29
34
|
const {
|
|
30
35
|
fileTransfers
|
|
31
36
|
} = store
|
|
37
|
+
this.pendingInitIds = new Set(
|
|
38
|
+
Array.from(this.pendingInitIds).filter(id => {
|
|
39
|
+
const transfer = fileTransfers.find(t => t.id === id)
|
|
40
|
+
return transfer && transfer.inited !== true
|
|
41
|
+
})
|
|
42
|
+
)
|
|
32
43
|
|
|
33
44
|
// First loop: Handle same type transfers
|
|
34
45
|
for (const t of fileTransfers) {
|
|
@@ -57,7 +68,7 @@ export default class TransportsActionStore extends Component {
|
|
|
57
68
|
inited,
|
|
58
69
|
pausing
|
|
59
70
|
} = t
|
|
60
|
-
return typeTo !== typeFrom && inited && pausing !== true
|
|
71
|
+
return typeTo !== typeFrom && (inited || this.pendingInitIds.has(t.id)) && pausing !== true
|
|
61
72
|
}).length
|
|
62
73
|
|
|
63
74
|
if (count >= maxTransport) {
|
|
@@ -80,7 +91,7 @@ export default class TransportsActionStore extends Component {
|
|
|
80
91
|
|
|
81
92
|
const isTransfer = typeTo !== typeFrom
|
|
82
93
|
|
|
83
|
-
if (inited || !isTransfer) {
|
|
94
|
+
if (inited || this.pendingInitIds.has(id) || !isTransfer) {
|
|
84
95
|
continue
|
|
85
96
|
}
|
|
86
97
|
|
|
@@ -95,6 +106,7 @@ export default class TransportsActionStore extends Component {
|
|
|
95
106
|
|
|
96
107
|
if (count < maxTransport) {
|
|
97
108
|
count++
|
|
109
|
+
this.pendingInitIds.add(id)
|
|
98
110
|
refsStatic.get('transfer-queue')?.addToQueue(
|
|
99
111
|
'update',
|
|
100
112
|
id,
|
|
@@ -31,7 +31,7 @@ export default auto(function CmdHistory (props) {
|
|
|
31
31
|
|
|
32
32
|
function handleDeleteCommand (cmd, ev) {
|
|
33
33
|
ev.stopPropagation()
|
|
34
|
-
|
|
34
|
+
window.store.deleteCmdHistory(cmd)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function handleCopyCommand (cmd, ev) {
|
|
@@ -54,9 +54,7 @@ export default auto(function CmdHistory (props) {
|
|
|
54
54
|
setKeyword(e.target.value)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const historyArray =
|
|
58
|
-
.map(([cmd, info]) => ({ cmd, ...info }))
|
|
59
|
-
.reverse()
|
|
57
|
+
const historyArray = (terminalCommandHistory || []).slice().reverse()
|
|
60
58
|
|
|
61
59
|
let filtered = filterArray(historyArray, keyword)
|
|
62
60
|
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
|
|
26
26
|
.item-list-unit.dragover
|
|
27
27
|
border: 1px dashed var(--primary)
|
|
28
|
+
.qm-item-dragover
|
|
29
|
+
border-left 2px solid var(--primary)
|
|
30
|
+
.qm-drag-handle
|
|
31
|
+
cursor grab
|
|
32
|
+
.qm-field-dragging
|
|
33
|
+
opacity 0.4
|
|
34
|
+
.qm-field-dragover
|
|
35
|
+
border-top 2px solid var(--primary)
|
|
28
36
|
.qm-label-select
|
|
29
37
|
min-width 120px
|
|
30
38
|
|
|
@@ -23,6 +23,8 @@ export default class QuickCommandsItem extends PureComponent {
|
|
|
23
23
|
draggable,
|
|
24
24
|
handleDragOver,
|
|
25
25
|
handleDragStart,
|
|
26
|
+
handleDragEnter,
|
|
27
|
+
handleDragLeave,
|
|
26
28
|
handleDrop
|
|
27
29
|
} = this.props
|
|
28
30
|
const cls = classNames('qm-item mg1r mg1b')
|
|
@@ -34,6 +36,8 @@ export default class QuickCommandsItem extends PureComponent {
|
|
|
34
36
|
draggable,
|
|
35
37
|
onDragOver: handleDragOver,
|
|
36
38
|
onDragStart: handleDragStart,
|
|
39
|
+
onDragEnter: handleDragEnter,
|
|
40
|
+
onDragLeave: handleDragLeave,
|
|
37
41
|
onDrop: handleDrop
|
|
38
42
|
}
|
|
39
43
|
return (
|
|
@@ -87,6 +87,14 @@ export default function QuickCommandsFooterBox (props) {
|
|
|
87
87
|
e.dataTransfer.setData('idDragged', e.target.getAttribute('data-id'))
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
function onDragEnter (e) {
|
|
91
|
+
e.target.closest('.qm-item')?.classList.add('qm-item-dragover')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function onDragLeave (e) {
|
|
95
|
+
e.target.closest('.qm-item')?.classList.remove('qm-item-dragover')
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
function onDrop (e) {
|
|
91
99
|
onDropFunc(e, '.qm-item')
|
|
92
100
|
}
|
|
@@ -116,6 +124,8 @@ export default function QuickCommandsFooterBox (props) {
|
|
|
116
124
|
draggable={!qmSortByFrequency}
|
|
117
125
|
handleDragOver={onDragOver}
|
|
118
126
|
handleDragStart={onDragStart}
|
|
127
|
+
handleDragEnter={onDragEnter}
|
|
128
|
+
handleDragLeave={onDragLeave}
|
|
119
129
|
handleDrop={onDrop}
|
|
120
130
|
/>
|
|
121
131
|
)
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
Button,
|
|
6
6
|
Input
|
|
7
7
|
} from 'antd'
|
|
8
|
-
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'
|
|
8
|
+
import { MinusCircleOutlined, PlusOutlined, HolderOutlined } from '@ant-design/icons'
|
|
9
9
|
import HelpIcon from '../common/help-icon'
|
|
10
10
|
import { copy } from '../../common/clipboard'
|
|
11
11
|
import { useRef } from 'react'
|
|
@@ -14,15 +14,70 @@ const FormItem = Form.Item
|
|
|
14
14
|
const FormList = Form.List
|
|
15
15
|
const e = window.translate
|
|
16
16
|
|
|
17
|
-
export default function renderQm () {
|
|
17
|
+
export default function renderQm (form) {
|
|
18
18
|
const focused = useRef(0)
|
|
19
|
-
|
|
19
|
+
const dragIndexRef = useRef(null)
|
|
20
|
+
|
|
21
|
+
function handleDragStart (e, index) {
|
|
22
|
+
dragIndexRef.current = index
|
|
23
|
+
e.target.closest('.ant-space-compact')?.classList.add('qm-field-dragging')
|
|
24
|
+
e.dataTransfer.effectAllowed = 'move'
|
|
25
|
+
e.dataTransfer.setData('text/plain', String(index))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function handleDragOver (e, index) {
|
|
29
|
+
e.preventDefault()
|
|
30
|
+
e.dataTransfer.dropEffect = 'move'
|
|
31
|
+
const el = e.target.closest('.ant-space-compact')
|
|
32
|
+
if (dragIndexRef.current !== index && el) {
|
|
33
|
+
el.classList.add('qm-field-dragover')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function handleDragLeave (e) {
|
|
38
|
+
e.target.closest('.ant-space-compact')?.classList.remove('qm-field-dragover')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handleDrop (e, index, form) {
|
|
42
|
+
e.preventDefault()
|
|
43
|
+
const el = e.target.closest('.ant-space-compact')
|
|
44
|
+
el?.classList.remove('qm-field-dragover')
|
|
45
|
+
const dragIndex = dragIndexRef.current
|
|
46
|
+
if (dragIndex === null || dragIndex === index) {
|
|
47
|
+
dragIndexRef.current = null
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
const commands = form.getFieldValue('commands') || []
|
|
51
|
+
const item = commands[dragIndex]
|
|
52
|
+
const newCommands = [...commands]
|
|
53
|
+
newCommands.splice(dragIndex, 1)
|
|
54
|
+
newCommands.splice(index, 0, item)
|
|
55
|
+
form.setFieldValue('commands', newCommands)
|
|
56
|
+
dragIndexRef.current = null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function handleDragEnd (e) {
|
|
60
|
+
const el = e.target.closest('.ant-space-compact')
|
|
61
|
+
el?.classList.remove('qm-field-dragging')
|
|
62
|
+
el?.classList.remove('qm-field-dragover')
|
|
63
|
+
dragIndexRef.current = null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function renderItem (field, i, add, remove, form) {
|
|
20
67
|
return (
|
|
21
68
|
<Space.Compact
|
|
22
69
|
align='center'
|
|
23
70
|
className='width-100 mg2b'
|
|
24
71
|
key={field.key}
|
|
72
|
+
draggable
|
|
73
|
+
onDragStart={(e) => handleDragStart(e, i)}
|
|
74
|
+
onDragOver={(e) => handleDragOver(e, i)}
|
|
75
|
+
onDragLeave={handleDragLeave}
|
|
76
|
+
onDrop={(e) => handleDrop(e, i, form)}
|
|
77
|
+
onDragEnd={handleDragEnd}
|
|
25
78
|
>
|
|
79
|
+
<HolderOutlined className='mg1r qm-drag-handle' />
|
|
80
|
+
|
|
26
81
|
<Space.Addon>{e('delay')}</Space.Addon>
|
|
27
82
|
<FormItem
|
|
28
83
|
label=''
|
|
@@ -119,7 +174,7 @@ export default function renderQm () {
|
|
|
119
174
|
<>
|
|
120
175
|
{
|
|
121
176
|
fields.map((field, i) => {
|
|
122
|
-
return renderItem(field, i, add, remove)
|
|
177
|
+
return renderItem(field, i, add, remove, form)
|
|
123
178
|
})
|
|
124
179
|
}
|
|
125
180
|
<FormItem>
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import List from '../setting-panel/list'
|
|
6
|
-
import { PlusOutlined } from '@ant-design/icons'
|
|
6
|
+
import { PlusOutlined, CopyOutlined } from '@ant-design/icons'
|
|
7
7
|
import { Select } from 'antd'
|
|
8
8
|
import classnames from 'classnames'
|
|
9
9
|
import highlight from '../common/highlight'
|
|
10
10
|
import QmTransport from './quick-command-transport'
|
|
11
11
|
import onDrop from './on-drop'
|
|
12
|
+
import copy from 'json-deep-copy'
|
|
13
|
+
import uid from '../../common/uid'
|
|
12
14
|
|
|
13
15
|
const { Option } = Select
|
|
14
16
|
const e = window.translate
|
|
@@ -45,17 +47,44 @@ export default class QuickCommandsList extends List {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
handleDragEnter = e => {
|
|
48
|
-
e.target.closest('.item-list-unit').classList.add('dragover')
|
|
50
|
+
e.target.closest('.item-list-unit').classList.add('qm-field-dragover')
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
handleDragLeave = e => {
|
|
52
|
-
e.target.closest('.item-list-unit').classList.remove('dragover')
|
|
54
|
+
e.target.closest('.item-list-unit').classList.remove('qm-field-dragover')
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
handleDrop = e => {
|
|
56
58
|
onDrop(e, '.item-list-unit')
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
duplicateItem = (e, item) => {
|
|
62
|
+
e.stopPropagation()
|
|
63
|
+
const { store } = window
|
|
64
|
+
const newCommand = copy(item)
|
|
65
|
+
newCommand.id = uid()
|
|
66
|
+
const baseName = item.name.replace(/\(\d+\)$/, '')
|
|
67
|
+
const sameNameCount = store.currentQuickCommands.filter(
|
|
68
|
+
cmd => cmd.name && cmd.name.replace(/\(\d+\)$/, '').includes(baseName)
|
|
69
|
+
).length
|
|
70
|
+
const duplicateIndex = sameNameCount > 0 ? sameNameCount : 1
|
|
71
|
+
newCommand.name = baseName + '(' + duplicateIndex + ')'
|
|
72
|
+
store.addQuickCommand(newCommand)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
renderDuplicateBtn = (item) => {
|
|
76
|
+
if (!item.id) {
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
return (
|
|
80
|
+
<CopyOutlined
|
|
81
|
+
title={e('duplicate')}
|
|
82
|
+
className='pointer list-item-duplicate'
|
|
83
|
+
onClick={(e) => this.duplicateItem(e, item)}
|
|
84
|
+
/>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
59
88
|
renderItem = (item, i) => {
|
|
60
89
|
if (!item) {
|
|
61
90
|
return null
|
|
@@ -94,6 +123,7 @@ export default class QuickCommandsList extends List {
|
|
|
94
123
|
}
|
|
95
124
|
{title}
|
|
96
125
|
</div>
|
|
126
|
+
{this.renderDuplicateBtn(item)}
|
|
97
127
|
{this.renderDelBtn(item)}
|
|
98
128
|
</div>
|
|
99
129
|
)
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
.list-item-apply
|
|
8
8
|
.list-item-remove
|
|
9
9
|
.list-item-bookmark
|
|
10
|
+
.list-item-duplicate
|
|
10
11
|
display none
|
|
11
12
|
width 24px
|
|
12
13
|
line-height 35px
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
.list-item-edit
|
|
36
37
|
.list-item-remove
|
|
37
38
|
.list-item-bookmark
|
|
39
|
+
.list-item-duplicate
|
|
38
40
|
display block
|
|
39
41
|
.theme-item:hover
|
|
40
42
|
.list-item-remove
|
|
@@ -49,4 +51,6 @@
|
|
|
49
51
|
// right 20px
|
|
50
52
|
.item-list-unit
|
|
51
53
|
.list-item-bookmark
|
|
52
|
-
right 18px
|
|
54
|
+
right 18px
|
|
55
|
+
.list-item-duplicate
|
|
56
|
+
right 24px
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { syncTypes } from '../../common/constants'
|
|
1
2
|
import { useState } from 'react'
|
|
2
3
|
import { LoadingOutlined, ReloadOutlined } from '@ant-design/icons'
|
|
3
4
|
import dayjs from 'dayjs'
|
|
@@ -10,7 +11,7 @@ export default function ServerDataStatus (props) {
|
|
|
10
11
|
const [loading, setLoading] = useState(false)
|
|
11
12
|
const token = store.getSyncToken(type)
|
|
12
13
|
const gistId = store.getSyncGistId(type)
|
|
13
|
-
const canSync = token && (gistId || type === 'custom' || type === 'cloud')
|
|
14
|
+
const canSync = token && (gistId || type === 'custom' || type === 'cloud' || type === syncTypes.webdav)
|
|
14
15
|
|
|
15
16
|
async function handleReload () {
|
|
16
17
|
setLoading(true)
|