Skip to content

Commit

Permalink
export to f5a/f5m; import from f5m (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj committed Jun 21, 2024
1 parent 46e50d2 commit 5f503a2
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 1 deletion.
Binary file modified assets/zh-Hans.lproj/Localizable.strings
Binary file not shown.
2 changes: 1 addition & 1 deletion src/config/about.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ struct AboutView: View {
.resizable()
.frame(width: 80, height: 80)
}
Text("Fcitx5 macOS")
Text(String("Fcitx5 macOS")) // no i18n by design
.font(.title)

Spacer().frame(height: gapSize)
Expand Down
119 changes: 119 additions & 0 deletions src/config/datamanager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,34 @@ import UniformTypeIdentifiers

let extractDir = cacheDir.appendingPathComponent("import")
let extractPath = extractDir.localPath()
let composeDir = cacheDir.appendingPathComponent("export")

private func extractZip(_ file: URL) -> Bool {
// unzip is unfriendly with Chinese file names
return exec("/usr/bin/ditto", ["-xk", file.localPath(), extractPath])
}

private func getTimeString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH_mm_ss'Z'"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
return dateFormatter.string(from: Date())
}

struct DataView: View {
@State private var openPanel = NSOpenPanel()
@AppStorage("ImportDataSelectedDirectory") var importDataSelectedDirectory: String?
@AppStorage("ExportDataSelectedDirectory") var exportDataSelectedDirectory: String?
@State private var showImportF5a = false
@State private var showImportF5m = false
@State private var showImportSquirrel = false
@State private var showImportHamster = false
@State private var showSquirrelError = false
@State private var showInvalidZip = false
@State private var showRunning = false
@State private var showExportSuccess = false
@State private var showExportFailure = false

private func importZip(_ binding: Binding<Bool>, _ validator: @escaping () -> Bool) {
// Keep a single openPanel to avoid confusion.
Expand Down Expand Up @@ -45,6 +59,44 @@ struct DataView: View {
}
}

private func exportZip(_ name: String) -> Bool {
_ = removeFile(composeDir)
// Fake f5a structure.
for name in ["databases", "external", "recently_used", "shared_prefs"] {
mkdirP(composeDir.appendingPathComponent(name).localPath())
}
let externalDir = composeDir.appendingPathComponent("external")
for operation in [
{ copyFile(configDir, externalDir.appendingPathComponent("config")) },
{ copyFile(localDir, externalDir.appendingPathComponent("data")) },
{
writeUTF8(
composeDir.appendingPathComponent("metadata.json"),
"""
{
"packageName": "org.fcitx.fcitx5.android",
"versionCode": 0,
"versionName": "",
"exportTime": \(Int(Date().timeIntervalSince1970 * 1000))
}\n
""")
},
{
exec(
"/bin/zsh",
[
"-c",
"cd \(quote(composeDir.localPath())) && /usr/bin/zip -r \(name) * -x \"*.DS_Store\"",
])
},
] {
if !operation() {
return false
}
}
return true
}

var body: some View {
VStack {
Text("Import data from …")
Expand Down Expand Up @@ -75,6 +127,18 @@ struct DataView: View {
ImportDataView().load(f5aItems)
}

Button {
importZip(
$showImportF5m,
{
extractDir.appendingPathComponent("metadata.json").exists()
})
} label: {
Text("Fcitx5 macOS").tooltip("fcitx5-macos_YYYY-MM-DD*.zip")
}.sheet(isPresented: $showImportF5m) {
ImportDataView().load(f5mItems)
}

Button {
importZip(
$showImportHamster,
Expand All @@ -86,6 +150,46 @@ struct DataView: View {
}.sheet(isPresented: $showImportHamster) {
ImportDataView().load(hamsterItems)
}

Spacer().frame(height: gapSize)

Text("Export data to …")

Button {
showRunning = true
let name = "fcitx5-macos_\(getTimeString()).zip"
DispatchQueue.global().async {
let res = exportZip(name)
DispatchQueue.main.async {
showRunning = false
if res {
if openPanel.isVisible {
openPanel.cancel(nil)
openPanel = NSOpenPanel()
}
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = true
openPanel.canChooseFiles = false
if openPanel.runModal() == .OK {
if let url = openPanel.url {
if moveFile(
composeDir.appendingPathComponent(name), url.appendingPathComponent(name))
{
showExportSuccess = true
} else {
showExportFailure = true
}
exportDataSelectedDirectory = url.localPath()
}
}
} else {
showExportFailure = true
}
}
}
} label: {
Text("Fcitx5 Android/macOS")
}
}.padding()
.toast(isPresenting: $showSquirrelError) {
AlertToast(
Expand All @@ -97,5 +201,20 @@ struct DataView: View {
displayMode: .hud, type: .error(Color.red),
title: NSLocalizedString("Invalid zip", comment: ""))
}
.toast(isPresenting: $showExportSuccess) {
AlertToast(
displayMode: .hud,
type: .complete(Color.green), title: NSLocalizedString("Export succeeded", comment: ""))
}
.toast(isPresenting: $showExportFailure) {
AlertToast(
displayMode: .hud, type: .error(Color.red),
title: NSLocalizedString("Export failed", comment: ""))
}
.toast(
isPresenting: $showRunning
) {
AlertToast(type: .loading)
}
}
}
22 changes: 22 additions & 0 deletions src/config/importdata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,28 @@ let f5aItems = [
importableRimeUser(f5aRimeDir),
]

let f5mItems = [
ImportableItem(
name: NSLocalizedString("Config", comment: ""), enabled: true,
exists: {
dataDir.appendingPathComponent("config").exists()
},
doImport: {
mkdirP(configDir.localPath())
return moveAndMerge(dataDir.appendingPathComponent("config"), configDir)
}),
ImportableItem(
name: NSLocalizedString("Data", comment: ""), enabled: true,
exists: {
dataDir.appendingPathComponent("data").exists()
},
doImport: {
mkdirP(localDir.localPath())
return moveAndMerge(
dataDir.appendingPathComponent("data"), localDir)
}),
]

class ImportDataVM: ObservableObject {
@Published var importableItems = [ImportableItem]()
@Published var items = [ImportableItem]()
Expand Down

0 comments on commit 5f503a2

Please sign in to comment.