feat: support import (#127)

This commit is contained in:
hstyi
2025-01-24 16:45:36 +08:00
committed by GitHub
parent 3d0ef2a331
commit f385f4b277
5 changed files with 148 additions and 14 deletions

View File

@@ -4,9 +4,14 @@ import app.termora.AES.encodeBase64String
import app.termora.Application.ohMyJson
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.highlight.KeywordHighlight
import app.termora.highlight.KeywordHighlightManager
import app.termora.keymap.Keymap
import app.termora.keymap.KeymapManager
import app.termora.keymap.KeymapPanel
import app.termora.keymgr.KeyManager
import app.termora.keymgr.OhKeyPair
import app.termora.macro.Macro
import app.termora.macro.MacroManager
import app.termora.native.FileChooser
import app.termora.sync.SyncConfig
@@ -28,12 +33,11 @@ import com.sun.jna.LastErrorException
import kotlinx.coroutines.*
import kotlinx.coroutines.swing.Swing
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.put
import kotlinx.serialization.json.*
import org.apache.commons.io.IOUtils
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.SystemUtils
import org.apache.commons.lang3.exception.ExceptionUtils
import org.apache.commons.lang3.time.DateFormatUtils
import org.jdesktop.swingx.JXEditorPane
import org.slf4j.LoggerFactory
@@ -55,6 +59,11 @@ import kotlin.time.Duration.Companion.milliseconds
class SettingsOptionsPane : OptionsPane() {
private val owner get() = SwingUtilities.getWindowAncestor(this@SettingsOptionsPane)
private val database get() = Database.getDatabase()
private val hostManager get() = HostManager.getInstance()
private val keymapManager get() = KeymapManager.getInstance()
private val macroManager get() = MacroManager.getInstance()
private val keywordHighlightManager get() = KeywordHighlightManager.getInstance()
private val keyManager get() = KeyManager.getInstance()
companion object {
private val log = LoggerFactory.getLogger(SettingsOptionsPane::class.java)
@@ -499,6 +508,7 @@ class SettingsOptionsPane : OptionsPane() {
val domainTextField = OutlineTextField(255)
val uploadConfigButton = JButton(I18n.getString("termora.settings.sync.push"), Icons.upload)
val exportConfigButton = JButton(I18n.getString("termora.settings.sync.export"), Icons.export)
val importConfigButton = JButton(I18n.getString("termora.settings.sync.import"), Icons.import)
val downloadConfigButton = JButton(I18n.getString("termora.settings.sync.pull"), Icons.download)
val lastSyncTimeLabel = JLabel()
val sync get() = database.sync
@@ -610,6 +620,7 @@ class SettingsOptionsPane : OptionsPane() {
}
exportConfigButton.addActionListener { export() }
importConfigButton.addActionListener { import() }
keysCheckBox.addActionListener { refreshButtons() }
hostsCheckBox.addActionListener { refreshButtons() }
@@ -626,6 +637,7 @@ class SettingsOptionsPane : OptionsPane() {
|| keywordHighlightsCheckBox.isSelected
uploadConfigButton.isEnabled = downloadConfigButton.isEnabled
exportConfigButton.isEnabled = downloadConfigButton.isEnabled
importConfigButton.isEnabled = downloadConfigButton.isEnabled
}
private fun export() {
@@ -641,6 +653,109 @@ class SettingsOptionsPane : OptionsPane() {
}
}
private fun import() {
val fileChooser = FileChooser()
fileChooser.fileSelectionMode = JFileChooser.FILES_ONLY
fileChooser.osxAllowedFileTypes = listOf("json")
fileChooser.win32Filters.add(Pair("JSON files", listOf("json")))
fileChooser.showOpenDialog(owner).thenAccept { files ->
if (files.isNotEmpty()) {
SwingUtilities.invokeLater { importFromFile(files.first()) }
}
}
}
private fun importFromFile(file: File) {
if (!file.exists()) {
return
}
val ranges = getSyncConfig().ranges
if (ranges.isEmpty()) {
return
}
// 最大 100MB
if (file.length() >= 1024 * 1024 * 100) {
OptionPane.showMessageDialog(
owner, I18n.getString("termora.settings.sync.import.file-too-large"),
messageType = JOptionPane.ERROR_MESSAGE
)
return
}
val text = file.readText()
val jsonResult = ohMyJson.runCatching { decodeFromString<JsonObject>(text) }
if (jsonResult.isFailure) {
val e = jsonResult.exceptionOrNull() ?: return
OptionPane.showMessageDialog(
owner, ExceptionUtils.getRootCauseMessage(e),
messageType = JOptionPane.ERROR_MESSAGE
)
return
}
val json = jsonResult.getOrNull() ?: return
if (ranges.contains(SyncRange.Hosts)) {
val hosts = json["hosts"]
if (hosts is JsonArray) {
ohMyJson.runCatching { decodeFromJsonElement<List<Host>>(hosts.jsonArray) }.onSuccess {
for (host in it) {
hostManager.addHost(host)
}
}
}
}
if (ranges.contains(SyncRange.KeyPairs)) {
val keyPairs = json["keyPairs"]
if (keyPairs is JsonArray) {
ohMyJson.runCatching { decodeFromJsonElement<List<OhKeyPair>>(keyPairs.jsonArray) }.onSuccess {
for (keyPair in it) {
keyManager.addOhKeyPair(keyPair)
}
}
}
}
if (ranges.contains(SyncRange.KeywordHighlights)) {
val keywordHighlights = json["keywordHighlights"]
if (keywordHighlights is JsonArray) {
ohMyJson.runCatching { decodeFromJsonElement<List<KeywordHighlight>>(keywordHighlights.jsonArray) }
.onSuccess {
for (keyPair in it) {
keywordHighlightManager.addKeywordHighlight(keyPair)
}
}
}
}
if (ranges.contains(SyncRange.Macros)) {
val macros = json["macros"]
if (macros is JsonArray) {
ohMyJson.runCatching { decodeFromJsonElement<List<Macro>>(macros.jsonArray) }.onSuccess {
for (macro in it) {
macroManager.addMacro(macro)
}
}
}
}
if (ranges.contains(SyncRange.Keymap)) {
val keymaps = json["keymaps"]
if (keymaps is JsonArray) {
for (keymap in keymaps.jsonArray.mapNotNull { Keymap.fromJSON(it.jsonObject) }) {
keymapManager.addKeymap(keymap)
}
}
}
OptionPane.showMessageDialog(
owner, I18n.getString("termora.settings.sync.import.successful"),
messageType = JOptionPane.INFORMATION_MESSAGE
)
}
private fun exportText(file: File) {
val syncConfig = getSyncConfig()
val text = ohMyJson.encodeToString(buildJsonObject {
@@ -651,21 +766,29 @@ class SettingsOptionsPane : OptionsPane() {
put("os", SystemUtils.OS_NAME)
put("exportDateHuman", DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.format(Date(now)))
if (syncConfig.ranges.contains(SyncRange.Hosts)) {
put("hosts", ohMyJson.encodeToJsonElement(HostManager.getInstance().hosts()))
put("hosts", ohMyJson.encodeToJsonElement(hostManager.hosts()))
}
if (syncConfig.ranges.contains(SyncRange.KeyPairs)) {
put("keyPairs", ohMyJson.encodeToJsonElement(KeyManager.getInstance().getOhKeyPairs()))
put("keyPairs", ohMyJson.encodeToJsonElement(keyManager.getOhKeyPairs()))
}
if (syncConfig.ranges.contains(SyncRange.KeywordHighlights)) {
put(
"keywordHighlights",
ohMyJson.encodeToJsonElement(KeywordHighlightManager.getInstance().getKeywordHighlights())
ohMyJson.encodeToJsonElement(keywordHighlightManager.getKeywordHighlights())
)
}
if (syncConfig.ranges.contains(SyncRange.Macros)) {
put(
"macros",
ohMyJson.encodeToJsonElement(MacroManager.getInstance().getMacros())
ohMyJson.encodeToJsonElement(macroManager.getMacros())
)
}
if (syncConfig.ranges.contains(SyncRange.Keymap)) {
val keymaps = keymapManager.getKeymaps().filter { !it.isReadonly }
.map { it.toJSONObject() }
put(
"keymaps",
ohMyJson.encodeToJsonElement(keymaps)
)
}
put("settings", buildJsonObject {
@@ -710,6 +833,7 @@ class SettingsOptionsPane : OptionsPane() {
)
}
@Suppress("DuplicatedCode")
private suspend fun pushOrPull(push: Boolean) {
if (typeComboBox.selectedItem == SyncType.GitLab) {
@@ -765,6 +889,7 @@ class SettingsOptionsPane : OptionsPane() {
withContext(Dispatchers.Swing) {
exportConfigButton.isEnabled = false
importConfigButton.isEnabled = false
downloadConfigButton.isEnabled = false
uploadConfigButton.isEnabled = false
typeComboBox.isEnabled = false
@@ -800,6 +925,7 @@ class SettingsOptionsPane : OptionsPane() {
withContext(Dispatchers.Swing) {
downloadConfigButton.isEnabled = true
exportConfigButton.isEnabled = true
importConfigButton.isEnabled = true
uploadConfigButton.isEnabled = true
keysCheckBox.isEnabled = true
hostsCheckBox.isEnabled = true
@@ -940,7 +1066,7 @@ class SettingsOptionsPane : OptionsPane() {
var rows = 1
val step = 2
val builder = FormBuilder.create().layout(layout).debug(false);
val builder = FormBuilder.create().layout(layout).debug(false)
val box = Box.createHorizontalBox()
box.add(typeComboBox)
if (typeComboBox.selectedItem == SyncType.GitLab) {
@@ -959,10 +1085,11 @@ class SettingsOptionsPane : OptionsPane() {
// Sync buttons
.add(
FormBuilder.create()
.layout(FormLayout("left:pref, $formMargin, left:pref, $formMargin, left:pref", "pref"))
.layout(FormLayout("pref, 2dlu, pref, 2dlu, pref, 2dlu, pref", "pref"))
.add(uploadConfigButton).xy(1, 1)
.add(downloadConfigButton).xy(3, 1)
.add(exportConfigButton).xy(5, 1)
.add(importConfigButton).xy(7, 1)
.build()
).xy(3, rows, "center, fill").apply { rows += step }
.add(lastSyncTimeLabel).xy(3, rows, "center, fill").apply { rows += step }
@@ -1057,8 +1184,6 @@ class SettingsOptionsPane : OptionsPane() {
private val tip = FlatLabel()
private val safeBtn = FlatButton()
private val doorman get() = Doorman.getInstance()
private val hostManager get() = HostManager.getInstance()
private val keyManager get() = KeyManager.getInstance()
init {
initView()

View File

@@ -17,6 +17,10 @@ class FileChooser {
var allowsOtherFileTypes = true
var canCreateDirectories = true
var win32Filters = mutableListOf<Pair<String, List<String>>>()
/**
* e.g. listOf("json")
*/
var osxAllowedFileTypes = emptyList<String>()
/**

View File

@@ -70,7 +70,10 @@ termora.settings.sync.push=Push
termora.settings.sync.push-warning=Pushing will overwrite the configuration. It is recommended to pull before pushing
termora.settings.sync.pull=Pull
termora.settings.sync.done=Synchronized data successfully
termora.settings.sync.export=Export
termora.settings.sync.export=${termora.keymgr.export}
termora.settings.sync.import=${termora.keymgr.import}
termora.settings.sync.import.file-too-large=The file is too large
termora.settings.sync.import.successful=Import data successfully
termora.settings.sync.export-done=The export was successful
termora.settings.sync.export-done-open-folder=The export was successful. Do you want to open the folder?
termora.settings.sync.range=Range

View File

@@ -74,13 +74,14 @@ termora.settings.sync=同步
termora.settings.sync.push=推送
termora.settings.sync.push-warning=推送将覆盖已有配置,建议先拉取再推送
termora.settings.sync.pull=拉取
termora.settings.sync.export=导出
termora.settings.sync.export-done=导出成功
termora.settings.sync.export-done-open-folder=导出成功,是否需要打开所在文件夹?
termora.settings.sync.range=范围
termora.settings.sync.range.keys=我的密钥
termora.settings.sync.last-sync-time=最后同步时间
termora.settings.sync.done=同步数据成功
termora.settings.sync.import.file-too-large=文件太大
termora.settings.sync.import.successful=导入数据成功
termora.settings.sync.gist=片段
termora.settings.sync.token=令牌
termora.settings.sync.type=类型

View File

@@ -78,13 +78,14 @@ termora.settings.sync=同步
termora.settings.sync.push=推送
termora.settings.sync.push-warning=推送將覆蓋先前的配置,建議先拉取再推送
termora.settings.sync.pull=拉取
termora.settings.sync.export=匯出
termora.settings.sync.export-done=匯出成功
termora.settings.sync.export-done-open-folder=匯出成功,是否需要打開所在資料夾?
termora.settings.sync.range=範圍
termora.settings.sync.range.keys=我的密鑰
termora.settings.sync.last-sync-time=最後同步時間
termora.settings.sync.done=同步資料成功
termora.settings.sync.import.file-too-large=檔案太大
termora.settings.sync.import.successful=導入資料成功
termora.settings.sync.gist=片段
termora.settings.sync.token=令牌
termora.settings.sync.type=類型