@electerm/electerm-react 1.38.19 → 1.38.40

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 (34) hide show
  1. package/client/common/build-ssh-tunnel.js +2 -1
  2. package/client/common/choose-save-folder.js +22 -0
  3. package/client/common/constants.js +1 -1
  4. package/client/common/default-setting.js +9 -1
  5. package/client/common/download.jsx +31 -0
  6. package/client/common/trzsz.js +2 -18
  7. package/client/components/bookmark-form/render-ssh-tunnel.jsx +32 -14
  8. package/client/components/main/main.jsx +4 -0
  9. package/client/components/quick-commands/quick-commands-box.jsx +20 -5
  10. package/client/components/quick-commands/quick-commands-form-elem.jsx +14 -17
  11. package/client/components/quick-commands/quick-commands-list-form.jsx +89 -0
  12. package/client/components/session/session.jsx +5 -4
  13. package/client/components/setting-panel/list.styl +3 -0
  14. package/client/components/setting-panel/setting-terminal.jsx +1 -0
  15. package/client/components/setting-panel/tree-list.styl +4 -0
  16. package/client/components/sftp/file-item.jsx +27 -20
  17. package/client/components/sftp/sftp-entry.jsx +65 -45
  18. package/client/components/shortcuts/shortcuts-defaults.js +7 -7
  19. package/client/components/tabs/index.jsx +12 -1
  20. package/client/components/terminal/attach-addon-custom.js +2 -2
  21. package/client/components/terminal/index.jsx +18 -15
  22. package/client/components/terminal-info/activity.jsx +4 -4
  23. package/client/components/terminal-info/base.jsx +58 -2
  24. package/client/components/terminal-info/disk.jsx +4 -3
  25. package/client/components/terminal-info/network.jsx +4 -3
  26. package/client/components/terminal-info/resource.jsx +20 -10
  27. package/client/components/terminal-info/up.jsx +5 -3
  28. package/client/css/basic.styl +3 -0
  29. package/client/store/common.js +51 -0
  30. package/client/store/index.js +2 -2
  31. package/client/store/sync.js +15 -2
  32. package/client/store/system-menu.js +6 -16
  33. package/package.json +1 -1
  34. package/client/common/download.js +0 -16
@@ -562,6 +562,22 @@ export default class Sftp extends Component {
562
562
  })
563
563
  }
564
564
 
565
+ sftpList = (sftp, remotePath) => {
566
+ return sftp.list(remotePath)
567
+ .then(arr => {
568
+ return arr.map(item => {
569
+ const { type } = item
570
+ return {
571
+ ...pick(item, ['name', 'size', 'accessTime', 'modifyTime', 'mode', 'owner', 'group']),
572
+ isDirectory: type === fileTypeMap.directory,
573
+ type: typeMap.remote,
574
+ path: remotePath,
575
+ id: generate()
576
+ }
577
+ })
578
+ })
579
+ }
580
+
565
581
  remoteList = async (
566
582
  returnList = false,
567
583
  remotePathReal,
@@ -644,52 +660,8 @@ export default class Sftp extends Component {
644
660
  }
645
661
  }
646
662
 
647
- let remote = await sftp.list(remotePath)
663
+ const remote = await this.sftpList(sftp, remotePath)
648
664
  this.sftp = sftp
649
- const remotes = deepCopy(remote)
650
- remote = []
651
- for (const _r of remotes) {
652
- const r = _r
653
- const { type, name } = r
654
- const f = {
655
- ...pick(r, ['name', 'size', 'accessTime', 'modifyTime', 'mode', 'owner', 'group']),
656
- isDirectory: r.type === fileTypeMap.directory,
657
- type: typeMap.remote,
658
- path: remotePath,
659
- id: generate()
660
- }
661
- if (type === fileTypeMap.link) {
662
- const linkPath = resolve(remotePath, name)
663
- let realpath = await sftp.readlink(linkPath)
664
- .catch(e => {
665
- console.debug(e)
666
- return null
667
- })
668
- if (!realpath) {
669
- continue
670
- }
671
- if (!isAbsPath(realpath)) {
672
- realpath = resolve(remotePath, realpath)
673
- realpath = await sftp.realpath(realpath)
674
- }
675
- const realFileInfo = await getRemoteFileInfo(
676
- sftp,
677
- realpath
678
- ).catch(e => {
679
- log.debug('seems a bad symbolic link')
680
- log.debug(e)
681
- return null
682
- })
683
- if (!realFileInfo) {
684
- continue
685
- }
686
- f.isSymbolicLink = true
687
- f.isDirectory = realFileInfo.isDirectory
688
- } else {
689
- f.isSymbolicLink = false
690
- }
691
- remote.push(f)
692
- }
693
665
  const update = {
694
666
  remote,
695
667
  remoteFileTree: buildTree(remote),
@@ -712,6 +684,7 @@ export default class Sftp extends Component {
712
684
  ]).slice(0, maxSftpHistory)
713
685
  }
714
686
  this.setState(update, () => {
687
+ this.updateRemoteList(remote, remotePath, sftp)
715
688
  this.props.editTab(tab.id, {
716
689
  sftpCreated: true
717
690
  })
@@ -731,6 +704,53 @@ export default class Sftp extends Component {
731
704
  }
732
705
  }
733
706
 
707
+ updateRemoteList = async (
708
+ remotes,
709
+ remotePath,
710
+ sftp
711
+ ) => {
712
+ const remote = []
713
+ for (const r of remotes) {
714
+ const { type, name } = r
715
+ if (type === fileTypeMap.link) {
716
+ const linkPath = resolve(remotePath, name)
717
+ let realpath = await sftp.readlink(linkPath)
718
+ .catch(e => {
719
+ console.debug(e)
720
+ return null
721
+ })
722
+ if (!realpath) {
723
+ continue
724
+ }
725
+ if (!isAbsPath(realpath)) {
726
+ realpath = resolve(remotePath, realpath)
727
+ realpath = await sftp.realpath(realpath)
728
+ }
729
+ const realFileInfo = await getRemoteFileInfo(
730
+ sftp,
731
+ realpath
732
+ ).catch(e => {
733
+ log.debug('seems a bad symbolic link')
734
+ log.debug(e)
735
+ return null
736
+ })
737
+ if (!realFileInfo) {
738
+ continue
739
+ }
740
+ r.isSymbolicLink = true
741
+ r.isDirectory = realFileInfo.isDirectory
742
+ } else {
743
+ r.isSymbolicLink = false
744
+ }
745
+ remote.push(r)
746
+ }
747
+ const update = {
748
+ remote,
749
+ remoteFileTree: buildTree(remote)
750
+ }
751
+ this.setState(update)
752
+ }
753
+
734
754
  localList = async (returnList = false, localPathReal, oldPath) => {
735
755
  if (!fs) return
736
756
  if (!returnList) {
@@ -50,13 +50,13 @@ export default () => {
50
50
  shortcut: 'ctrl+l,ctrl+shift+l',
51
51
  shortcutMac: 'meta+l'
52
52
  },
53
- {
54
- name: 'terminal_selectAll',
55
- shortcut: 'ctrl+a,ctrl+shift+a',
56
- shortcutMac: 'meta+a',
57
- skipMac: true,
58
- readonly: true
59
- },
53
+ // {
54
+ // name: 'terminal_selectAll',
55
+ // shortcut: 'ctrl+a,ctrl+shift+a',
56
+ // shortcutMac: 'meta+a',
57
+ // skipMac: true,
58
+ // readonly: true
59
+ // },
60
60
  {
61
61
  name: 'terminal_copy',
62
62
  shortcut: 'ctrl+c,ctrl+shift+c',
@@ -49,6 +49,7 @@ export default class Tabs extends React.Component {
49
49
  tabsRef
50
50
  } = this
51
51
  tabsRef.current.addEventListener('mousedown', this.handleClickEvent)
52
+ tabsRef.current.addEventListener('mousewheel', this.handleWheelEvent)
52
53
  }
53
54
 
54
55
  componentDidUpdate (prevProps) {
@@ -75,7 +76,7 @@ export default class Tabs extends React.Component {
75
76
  const len = tabs.length
76
77
  const addBtnWidth = 22
77
78
  const tabsWidth = this.tabsWidth()
78
- const tabsWidthAll = tabMargin * len + 216 + tabsWidth
79
+ const tabsWidthAll = tabMargin * len + 166 + tabsWidth
79
80
  return width < (tabsWidthAll + addBtnWidth)
80
81
  }
81
82
 
@@ -129,6 +130,16 @@ export default class Tabs extends React.Component {
129
130
  this.dom.scrollLeft = scrollLeft
130
131
  }
131
132
 
133
+ handleWheelEvent = (e) => {
134
+ if (this.isOverflow()) {
135
+ if (e.deltaY < 0) {
136
+ this.handleScrollLeft()
137
+ } else {
138
+ this.handleScrollRight()
139
+ }
140
+ }
141
+ }
142
+
132
143
  handleClickMenu = ({ key }) => {
133
144
  const id = key.split('##')[1]
134
145
  this.props.onChangeTabId(id)
@@ -55,7 +55,7 @@ export default class AttachAddonCustom extends AttachAddon {
55
55
  const data = ev.target.result
56
56
  const { term } = this
57
57
  const str = this.decoder.decode(data)
58
- if (term.parent.props.sftpPathFollowSsh && term.buffer.active.type !== 'alternate') {
58
+ if (term?.parent?.props.sftpPathFollowSsh && term?.buffer.active.type !== 'alternate') {
59
59
  const {
60
60
  cwdId
61
61
  } = term
@@ -78,7 +78,7 @@ export default class AttachAddonCustom extends AttachAddon {
78
78
  }
79
79
  term.write(nnss.join('\r'))
80
80
  } else {
81
- term.write(str)
81
+ term?.write(str)
82
82
  }
83
83
  }
84
84
 
@@ -234,10 +234,10 @@ class Term extends Component {
234
234
  this.onClear()
235
235
  }
236
236
 
237
- selectAllShortcut = (e) => {
238
- e.stopPropagation()
239
- this.term.selectAll()
240
- }
237
+ // selectAllShortcut = (e) => {
238
+ // e.stopPropagation()
239
+ // this.term.selectAll()
240
+ // }
241
241
 
242
242
  copyShortcut = (e) => {
243
243
  const sel = this.term.getSelection()
@@ -425,6 +425,9 @@ class Term extends Component {
425
425
  }
426
426
 
427
427
  webLinkHandler = (event, url) => {
428
+ if (event?.button === 2) {
429
+ return false
430
+ }
428
431
  if (!this.props.config.ctrlOrMetaOpenTerminalLink) {
429
432
  return window.openLink(url, '_blank')
430
433
  }
@@ -757,9 +760,9 @@ class Term extends Component {
757
760
  this.term.focus()
758
761
  }
759
762
 
760
- onSelectAll = () => {
761
- this.term.selectAll()
762
- }
763
+ // onSelectAll = () => {
764
+ // this.term.selectAll()
765
+ // }
763
766
 
764
767
  onClear = () => {
765
768
  this.term.clear()
@@ -810,7 +813,7 @@ class Term extends Component {
810
813
  const copyShortcut = this.getShortcut('terminal_copy')
811
814
  const pasteShortcut = this.getShortcut('terminal_paste')
812
815
  const clearShortcut = this.getShortcut('terminal_clear')
813
- const selectAllShortcut = this.getShortcut('terminal_selectAll')
816
+ // const selectAllShortcut = this.getShortcut('terminal_selectAll')
814
817
  const searchShortcut = this.getShortcut('terminal_search')
815
818
  return [
816
819
  {
@@ -833,12 +836,12 @@ class Term extends Component {
833
836
  text: e('clear'),
834
837
  subText: clearShortcut
835
838
  },
836
- {
837
- func: 'onSelectAll',
838
- icon: 'SelectOutlined',
839
- text: e('selectAll'),
840
- subText: selectAllShortcut
841
- },
839
+ // {
840
+ // func: 'onSelectAll',
841
+ // icon: 'SelectOutlined',
842
+ // text: e('selectAll'),
843
+ // subText: selectAllShortcut
844
+ // },
842
845
  {
843
846
  func: 'toggleSearch',
844
847
  icon: 'SearchOutlined',
@@ -1203,7 +1206,7 @@ class Term extends Component {
1203
1206
  }
1204
1207
 
1205
1208
  oncloseSocket = () => {
1206
- if (this.onClose) {
1209
+ if (this.onClose || !this.props.tab.enableSsh) {
1207
1210
  return
1208
1211
  }
1209
1212
  this.setStatus(
@@ -4,15 +4,15 @@
4
4
 
5
5
  import { Table, Tooltip, Popconfirm } from 'antd'
6
6
  import { isEmpty } from 'lodash-es'
7
- import { CloseCircleOutlined } from '@ant-design/icons'
7
+ import { CloseCircleOutlined, BarsOutlined } from '@ant-design/icons'
8
8
  import colsParser from './data-cols-parser'
9
9
 
10
10
  const { prefix } = window
11
11
  const m = prefix('menu')
12
12
 
13
13
  export default function TerminalInfoActivities (props) {
14
- const { activities } = props
15
- if (isEmpty(activities) || !props.isRemote) {
14
+ const { activities, isRemote, terminalInfos } = props
15
+ if (isEmpty(activities) || !isRemote || !terminalInfos.includes('activities')) {
16
16
  return null
17
17
  }
18
18
  const col = colsParser(activities[0])
@@ -47,7 +47,7 @@ export default function TerminalInfoActivities (props) {
47
47
  }
48
48
  return (
49
49
  <div className='terminal-info-section terminal-info-act'>
50
- <div className='pd1y bold'>Activities</div>
50
+ <div className='pd1y bold'><BarsOutlined /> Activities</div>
51
51
  <Table {...ps} />
52
52
  </div>
53
53
  )
@@ -7,15 +7,28 @@ import {
7
7
  commonActions
8
8
  } from '../../common/constants'
9
9
  import {
10
- Switch
10
+ Switch,
11
+ Space,
12
+ Button
11
13
  } from 'antd'
12
14
  import ShowItem from '../common/show-item'
15
+ import defaults from '../../common/default-setting'
13
16
  import postMsg from '../../common/post-msg'
14
17
  import { toggleTerminalLog, toggleTerminalLogTimestamp } from '../terminal/terminal-apis'
18
+ import { ClockCircleOutlined, BorderlessTableOutlined, DatabaseOutlined, BarsOutlined, ApiOutlined, PartitionOutlined } from '@ant-design/icons'
15
19
 
16
20
  const { prefix } = window
17
21
  const st = prefix('setting')
18
22
 
23
+ const mapper = {
24
+ uptime: <ClockCircleOutlined />,
25
+ cpu: <BorderlessTableOutlined />,
26
+ mem: <DatabaseOutlined />,
27
+ activities: <BarsOutlined />,
28
+ network: <ApiOutlined />,
29
+ disks: <PartitionOutlined />
30
+ }
31
+
19
32
  export default class TerminalInfoBase extends Component {
20
33
  state = {
21
34
  saveTerminalLogToFile: false,
@@ -54,6 +67,16 @@ export default class TerminalInfoBase extends Component {
54
67
  })
55
68
  }
56
69
 
70
+ toggleTerminalLogInfo = (h) => {
71
+ const { terminalInfos } = this.props
72
+ const nv = terminalInfos.includes(h)
73
+ ? terminalInfos.filter(f => f !== h)
74
+ : [...terminalInfos, h]
75
+ window.store.setConfig({
76
+ terminalInfos: nv
77
+ })
78
+ }
79
+
57
80
  handleToggle = () => {
58
81
  const { saveTerminalLogToFile, addTimeStampToTermLog } = this.state
59
82
  const {
@@ -120,10 +143,38 @@ export default class TerminalInfoBase extends Component {
120
143
  unCheckedChildren={name}
121
144
  checked={addTimeStampToTermLog}
122
145
  onChange={this.handleToggleTimestamp}
146
+ className='mg1b'
123
147
  />
124
148
  )
125
149
  }
126
150
 
151
+ renderInfoSelection () {
152
+ const {
153
+ terminalInfos
154
+ } = this.props
155
+ return (
156
+ <Space.Compact block>
157
+ {
158
+ defaults.terminalInfos.map(f => {
159
+ const type = terminalInfos.includes(f) ? 'primary' : 'default'
160
+ return (
161
+ <Button
162
+ key={f + 'term-info-sel'}
163
+ type={type}
164
+ size='small'
165
+ onClick={() => this.toggleTerminalLogInfo(f)}
166
+ className='cap'
167
+ icon={mapper[f]}
168
+ >
169
+ {f}
170
+ </Button>
171
+ )
172
+ })
173
+ }
174
+ </Space.Compact>
175
+ )
176
+ }
177
+
127
178
  render () {
128
179
  const {
129
180
  id,
@@ -149,13 +200,18 @@ export default class TerminalInfoBase extends Component {
149
200
  unCheckedChildren={name}
150
201
  checked={saveTerminalLogToFile}
151
202
  onChange={this.handleToggle}
152
- className='mg1r'
203
+ className='mg1r mg1b'
153
204
  />
154
205
  {
155
206
  this.renderTimestamp()
156
207
  }
157
208
  </span>
158
209
  </div>
210
+ <div className='pd2y'>
211
+ {
212
+ this.renderInfoSelection()
213
+ }
214
+ </div>
159
215
  <p><b>log:</b> {to}</p>
160
216
  </div>
161
217
  )
@@ -5,10 +5,11 @@
5
5
  import { Table } from 'antd'
6
6
  import { isEmpty } from 'lodash-es'
7
7
  import colsParser from './data-cols-parser'
8
+ import { PartitionOutlined } from '@ant-design/icons'
8
9
 
9
10
  export default function TerminalInfoDisk (props) {
10
- const { disks } = props
11
- if (isEmpty(disks) || !props.isRemote) {
11
+ const { disks, isRemote, terminalInfos } = props
12
+ if (isEmpty(disks) || !isRemote || !terminalInfos.includes('disks')) {
12
13
  return null
13
14
  }
14
15
  const col = colsParser(disks[0])
@@ -22,7 +23,7 @@ export default function TerminalInfoDisk (props) {
22
23
  }
23
24
  return (
24
25
  <div className='terminal-info-section terminal-info-disk'>
25
- <div className='pd1y bold'>File system</div>
26
+ <div className='pd1y bold'><PartitionOutlined /> File system</div>
26
27
  <Table {...ps} />
27
28
  </div>
28
29
  )
@@ -7,10 +7,11 @@ import { isEmpty } from 'lodash-es'
7
7
  import { useEffect, useState } from 'react'
8
8
  import { formatBytes } from '../../common/byte-format'
9
9
  import copy from 'json-deep-copy'
10
+ import { ApiOutlined } from '@ant-design/icons'
10
11
 
11
12
  export default function TerminalInfoDisk (props) {
12
- const { network } = props
13
- if (isEmpty(network) || !props.isRemote) {
13
+ const { network, isRemote, terminalInfos } = props
14
+ if (isEmpty(network) || !isRemote || !terminalInfos.includes('network')) {
14
15
  return null
15
16
  }
16
17
  const [state, setter] = useState({
@@ -107,7 +108,7 @@ export default function TerminalInfoDisk (props) {
107
108
  }
108
109
  return (
109
110
  <div className='terminal-info-section terminal-info-network'>
110
- <div className='pd1y bold'>Network</div>
111
+ <div className='pd1y bold'><ApiOutlined /> Network</div>
111
112
  <Table {...ps} />
112
113
  </div>
113
114
  )
@@ -25,8 +25,13 @@ function computePercent (used, total) {
25
25
  }
26
26
 
27
27
  export default function TerminalInfoResource (props) {
28
- const { cpu, mem, swap } = props
29
- if (!props.isRemote) {
28
+ const { cpu, mem, swap, isRemote, terminalInfos } = props
29
+ if (
30
+ !isRemote ||
31
+ (!terminalInfos.includes('cpu') &&
32
+ !terminalInfos.includes('mem') &&
33
+ !terminalInfos.includes('swap'))
34
+ ) {
30
35
  return null
31
36
  }
32
37
  function renderItem (obj) {
@@ -56,20 +61,25 @@ export default function TerminalInfoResource (props) {
56
61
  </div>
57
62
  )
58
63
  }
59
- const data = [
60
- {
64
+ const data = []
65
+ if (terminalInfos.includes('cpu')) {
66
+ data.push({
61
67
  name: 'cpu',
62
68
  percent: parseInt10(cpu)
63
- },
64
- {
69
+ })
70
+ }
71
+ if (terminalInfos.includes('mem')) {
72
+ data.push({
65
73
  name: 'mem',
66
74
  ...mem
67
- },
68
- {
75
+ })
76
+ }
77
+ if (terminalInfos.includes('swap')) {
78
+ data.push({
69
79
  name: 'swap',
70
80
  ...swap
71
- }
72
- ]
81
+ })
82
+ }
73
83
  return (
74
84
  <div className='terminal-info-section terminal-info-resource'>
75
85
  {
@@ -2,14 +2,16 @@
2
2
  * up time info
3
3
  */
4
4
 
5
+ import { ClockCircleOutlined } from '@ant-design/icons'
6
+
5
7
  export default function TerminalInfoUp (props) {
6
- const { uptime } = props
7
- if (!props.isRemote) {
8
+ const { uptime, isRemote, terminalInfos } = props
9
+ if (!isRemote || !terminalInfos.includes('uptime')) {
8
10
  return null
9
11
  }
10
12
  return (
11
13
  <div className='terminal-info-section terminal-info-up'>
12
- <b>uptime</b>: {uptime}
14
+ <b><ClockCircleOutlined /> uptime</b>: {uptime}
13
15
  </div>
14
16
  )
15
17
  }
@@ -39,3 +39,6 @@ body
39
39
 
40
40
  a
41
41
  color primary
42
+
43
+ .cap
44
+ text-transform capitalize
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import handleError from '../common/error-handler'
6
+ import { Modal } from 'antd'
6
7
  import { debounce } from 'lodash-es'
7
8
  import postMessage from '../common/post-msg'
8
9
  import {
@@ -15,6 +16,10 @@ import {
15
16
  } from '../common/constants'
16
17
  import * as ls from '../common/safe-local-storage'
17
18
 
19
+ const { prefix } = window
20
+ const m = prefix('menu')
21
+ const c = prefix('common')
22
+
18
23
  export default Store => {
19
24
  Store.prototype.storeAssign = function (updates) {
20
25
  Object.assign(this, updates)
@@ -155,4 +160,50 @@ export default Store => {
155
160
  ls.setItem(dismissDelKeyTipLsKey, 'y')
156
161
  window.store.hideDelKeyTip = true
157
162
  }
163
+ Store.prototype.beforeExit = function (e) {
164
+ const { confirmBeforeExit } = window.store.config
165
+ if (
166
+ (confirmBeforeExit &&
167
+ !window.confirmExit) ||
168
+ window.store.isTransporting
169
+ ) {
170
+ e.returnValue = false
171
+ let mod = null
172
+ mod = Modal.confirm({
173
+ onCancel: () => {
174
+ window.confirmExit = false
175
+ mod.destroy()
176
+ },
177
+ onOk: () => {
178
+ window.confirmExit = true
179
+ window.store[window.exitFunction]()
180
+ },
181
+ title: m('quit'),
182
+ okText: c('ok'),
183
+ cancelText: c('cancel'),
184
+ content: ''
185
+ })
186
+ }
187
+ }
188
+ Store.prototype.beforeExitApp = function (e, name) {
189
+ let mod = null
190
+ mod = Modal.confirm({
191
+ onCancel: () => {
192
+ window.pre.runGlobalAsync('setCloseAction', 'closeApp')
193
+ mod.destroy()
194
+ },
195
+ onOk: () => {
196
+ window.pre.runGlobalAsync(name)
197
+ },
198
+ title: m('quit'),
199
+ okText: c('ok'),
200
+ cancelText: c('cancel'),
201
+ content: ''
202
+ })
203
+ }
204
+ Store.prototype.setTerminalInfos = function (arr) {
205
+ window.store.setConfig({
206
+ terminalInfos: arr
207
+ })
208
+ }
158
209
  }
@@ -8,7 +8,7 @@ import loadDataExtend from './load-data'
8
8
  import dbUpgradeExtend from './db-upgrade'
9
9
  import eventExtend from './event'
10
10
  import syncExtend from './sync'
11
- import appUpgrdeExtend from './app-upgrade'
11
+ import appUpgradeExtend from './app-upgrade'
12
12
  import bookmarkGroupExtend from './bookmark-group'
13
13
  import bookmarkExtend from './bookmark'
14
14
  import commonExtend from './common'
@@ -288,7 +288,7 @@ loadDataExtend(Store)
288
288
  eventExtend(Store)
289
289
  dbUpgradeExtend(Store)
290
290
  syncExtend(Store)
291
- appUpgrdeExtend(Store)
291
+ appUpgradeExtend(Store)
292
292
  bookmarkGroupExtend(Store)
293
293
  bookmarkExtend(Store)
294
294
  commonExtend(Store)
@@ -2,12 +2,12 @@
2
2
  * sync data to github gist related
3
3
  */
4
4
 
5
- import { without, get, pick, debounce } from 'lodash-es'
5
+ import { without, get, pick, debounce, findIndex } from 'lodash-es'
6
6
  import copy from 'json-deep-copy'
7
7
  import {
8
8
  settingMap, packInfo, syncTypes
9
9
  } from '../common/constants'
10
- import { remove, dbNames, insert, update } from '../common/db'
10
+ import { remove, dbNames, insert, update, getData } from '../common/db'
11
11
  import fetch from '../common/fetch-from-server'
12
12
  import download from '../common/download'
13
13
  import { fixBookmarks } from '../common/db-fix'
@@ -169,6 +169,10 @@ export default (Store) => {
169
169
  objs[`${n}.json`] = {
170
170
  content: str
171
171
  }
172
+ const order = await getData(`${n}:order`)
173
+ objs[`${n}.order.json`] = {
174
+ content: JSON.stringify(order)
175
+ }
172
176
  }
173
177
  const res = await fetchData(type, 'update', [gistId, {
174
178
  description: 'sync electerm data',
@@ -235,6 +239,15 @@ export default (Store) => {
235
239
  } else if (n === settingMap.bookmarks) {
236
240
  arr = fixBookmarks(arr)
237
241
  }
242
+ let strOrder = get(gist, `files["${n}.order.json"].content`)
243
+ if (strOrder) {
244
+ strOrder = JSON.parse(strOrder)
245
+ arr.sort((a, b) => {
246
+ const ai = findIndex(strOrder, r => r === a.id)
247
+ const bi = findIndex(strOrder, r => r === b.id)
248
+ return ai - bi
249
+ })
250
+ }
238
251
  toInsert.push({
239
252
  name: n,
240
253
  value: arr