From e75ebed26999111dd719a7384ab6beea88179096 Mon Sep 17 00:00:00 2001 From: NI Date: Mon, 30 Dec 2019 18:44:01 +0800 Subject: [PATCH] Allowing "Known remotes" to be import and exported --- ui/commands/history.js | 95 +++++++++++++++++++++++++------- ui/home.vue | 8 +++ ui/widgets/connect.vue | 10 ++++ ui/widgets/connect_known.css | 28 ++++++++++ ui/widgets/connect_known.vue | 100 +++++++++++++++++++++++++++++++++- ui/widgets/connect_switch.vue | 17 +----- 6 files changed, 221 insertions(+), 37 deletions(-) diff --git a/ui/commands/history.js b/ui/commands/history.js index 5b6a8fe..92c1f66 100644 --- a/ui/commands/history.js +++ b/ui/commands/history.js @@ -32,6 +32,26 @@ export class History { this.saver = saver; } + /** + * Return the index of given uname, or -1 when not found + * + * @param {string} uname the unique name + * + * @returns {integer} The index of given uname + * + */ + indexOf(uname) { + for (let i in this.records) { + if (this.records[i].uname !== uname) { + continue; + } + + return i; + } + + return -1; + } + /** * Save record to history * @@ -44,13 +64,10 @@ export class History { * */ save(uname, title, lastUsed, info, data, sessionData) { - for (let i in this.records) { - if (this.records[i].uname !== uname) { - continue; - } + const unameIdx = this.indexOf(uname); - this.records.splice(i, 1); - break; + if (unameIdx >= 0) { + this.records.splice(unameIdx, 1); } this.records.push({ @@ -78,20 +95,7 @@ export class History { * */ store() { - let r = []; - - for (let i in this.records) { - r.push({ - uname: this.records[i].uname, - title: this.records[i].title, - type: this.records[i].type, - color: this.records[i].color, - last: this.records[i].last, - data: this.records[i].data - }); - } - - this.saver(this, r); + this.saver(this, this.export()); } /** @@ -131,7 +135,8 @@ export class History { } /** - * Return all history records + * Return all history records. The exported data is differ than the + * internal ones, it cannot be directly import back * * @returns {array} Records * @@ -153,4 +158,52 @@ export class History { return r; } + + /** + * Export current history records + * + * @returns {array} Records + * + */ + export() { + let r = []; + + for (let i in this.records) { + r.push({ + uname: this.records[i].uname, + title: this.records[i].title, + type: this.records[i].type, + color: this.records[i].color, + last: this.records[i].last, + data: this.records[i].data + }); + } + + return r; + } + + /** + * Import data into current history records + * + * @param {array} records Records + * + */ + import(records) { + for (let i in records) { + if (this.indexOf(records[i].uname) >= 0) { + continue; + } + + this.records.push({ + uname: records[i].uname, + title: records[i].title, + type: records[i].type, + color: records[i].color, + last: records[i].last, + data: records[i].data + }); + } + + this.store(); + } } diff --git a/ui/home.vue b/ui/home.vue index d0fa4ad..74af26b 100644 --- a/ui/home.vue +++ b/ui/home.vue @@ -112,6 +112,8 @@ :connectors="connector.connectors" :knowns="connector.knowns" :knowns-launcher-builder="buildknownLauncher" + :knowns-export="exportKnowns" + :knowns-import="importKnowns" :busy="connector.busy" @display="windows.connect = $event" @connector-select="connectNew" @@ -438,6 +440,12 @@ export default { return this.hostPath + "#+" + connector.launcher(known.data); }, + exportKnowns() { + return this.connector.historyRec.export(); + }, + importKnowns(d) { + return this.connector.historyRec.import(d); + }, removeKnown(uid) { this.connector.historyRec.del(uid); diff --git a/ui/widgets/connect.vue b/ui/widgets/connect.vue index 0ef0dc9..ebe7cd0 100644 --- a/ui/widgets/connect.vue +++ b/ui/widgets/connect.vue @@ -46,6 +46,8 @@ v-if="tab === 'known' && !inputting" :knowns="knowns" :launcher-builder="knownsLauncherBuilder" + :knowns-export="knownsExport" + :knowns-import="knownsImport" @select="selectKnown" @remove="removeKnown" @clear-session="clearSessionKnown" @@ -106,6 +108,14 @@ export default { type: Function, default: () => [] }, + knownsExport: { + type: Function, + default: () => [] + }, + knownsImport: { + type: Function, + default: () => [] + }, connectors: { type: Array, default: () => [] diff --git a/ui/widgets/connect_known.css b/ui/widgets/connect_known.css index 9957cc2..346996b 100644 --- a/ui/widgets/connect_known.css +++ b/ui/widgets/connect_known.css @@ -24,6 +24,34 @@ font-size: 0.75em; padding: 15px; background: #3a3a3a; + display: flex; + flex-direction: column; +} + +#connect-known-list-list, +#connect-known-list-empty { + flex: auto; +} + +#connect-known-list-empty { + text-align: center; + color: #999; + font-size: 1.2em; + display: flex; + align-items: center; + justify-content: center; +} + +#connect-known-list-import { + margin: 15px 0 10px 0; + color: #aaa; + font-size: 1.1em; + text-align: center; +} + +#connect-known-list-import a { + color: #e9a; + text-decoration: none; } #connect-known-list li { diff --git a/ui/widgets/connect_known.vue b/ui/widgets/connect_known.vue index 173defe..8c88ddd 100644 --- a/ui/widgets/connect_known.vue +++ b/ui/widgets/connect_known.vue @@ -19,7 +19,10 @@ @@ -78,6 +88,14 @@ export default { launcherBuilder: { type: Function, default: () => [] + }, + knownsExport: { + type: Function, + default: () => [] + }, + knownsImport: { + type: Function, + default: () => [] } }, data() { @@ -159,6 +177,86 @@ export default { } this.$emit("clear-session", uid); + }, + exportHosts() { + let el = null; + + try { + const dataStr = JSON.stringify(this.knownsExport()); + + el = document.createElement("a"); + el.setAttribute( + "href", + "data:text/plain;charset=utf-8," + btoa(dataStr) + ); + el.setAttribute("target", "_blank"); + el.setAttribute("download", "sshwifty.known-remotes.txt"); + el.setAttribute( + "style", + "overflow: hidden; opacity: 0; width: 1px; height: 1px; top: -1px;" + + "left: -1px; position: absolute;" + ); + + document.body.appendChild(el); + + el.click(); + } catch (e) { + alert("Unable to export known remotes: " + e); + } + + if (el === null) { + return; + } + + document.body.removeChild(el); + }, + importHosts() { + const self = this; + + let el = null; + + try { + el = document.createElement("input"); + el.setAttribute("type", "file"); + el.setAttribute( + "style", + "overflow: hidden; opacity: 0; width: 1px; height: 1px; top: -1px;" + + "left: -1px; position: absolute;" + ); + el.addEventListener("change", ev => { + const t = ev.target; + + if (t.files.length <= 0) { + return; + } + + t.disabled = "disabled"; + + let r = new FileReader(); + + r.onload = () => { + try { + self.knownsImport(JSON.parse(atob(r.result))); + } catch (e) { + alert("Unable to import known remotes due to error: " + e); + } + }; + + r.readAsText(t.files[0], "utf-8"); + }); + + document.body.appendChild(el); + + el.click(); + } catch (e) { + alert("Unable to load known remotes data due to error: " + e); + } + + if (el === null) { + return; + } + + document.body.removeChild(el); } } }; diff --git a/ui/widgets/connect_switch.vue b/ui/widgets/connect_switch.vue index cac63e9..c24f148 100644 --- a/ui/widgets/connect_switch.vue +++ b/ui/widgets/connect_switch.vue @@ -23,12 +23,8 @@ New remote -
  • - Known remotes - {{ knownsLength }} +
  • + Known remotes {{ knownsLength }}
  • @@ -47,15 +43,6 @@ export default { default: 0 } }, - watch: { - knownsLength(newVal) { - if (newVal > 0) { - return; - } - - this.switchTab("new"); - } - }, methods: { switchTab(to) { this.$emit("switch", to);