Skip to content

Commit 190d9f3

Browse files
committed
Importing and exporting in JSON possible
1 parent ebfe792 commit 190d9f3

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class AccessoryController: ObservableObject {
1717
var selfObserver: AnyCancellable?
1818
var listElementsObserver = [AnyCancellable]()
1919
let findMyController: FindMyController
20+
21+
weak var savePanel: NSSavePanel?
2022

2123
init(accessories: [Accessory], findMyController: FindMyController) {
2224
self.accessories = accessories
@@ -99,28 +101,65 @@ class AccessoryController: ObservableObject {
99101
let savePanel = NSSavePanel()
100102
// savePanel.allowedFileTypes = ["plist", "json"]
101103
if #available(macOS 12.0, *) {
102-
savePanel.allowedContentTypes = [.propertyList, .json]
104+
savePanel.allowedContentTypes = [.propertyList]
105+
}else {
106+
savePanel.allowedFileTypes = ["plist"]
103107
}
104-
savePanel.allowedFileTypes = ["plist", "json"]
108+
105109
savePanel.canCreateDirectories = true
106110
savePanel.directoryURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
107111
savePanel.message = "This export contains all private keys! Keep the file save to protect your location data"
108112
savePanel.nameFieldLabel = "Filename"
109-
savePanel.nameFieldStringValue = "openhaystack_accessories.plist"
113+
savePanel.nameFieldStringValue = "openhaystack_accessories"
110114
savePanel.prompt = "Export"
111115
savePanel.title = "Export accessories & keys"
112116
savePanel.isExtensionHidden = false
117+
118+
let accessoryView = NSView()
119+
let popUpButton = NSPopUpButton(title: "File type", target: self, action: #selector(exportFileTypeChanged(button:)))
120+
popUpButton.addItems(withTitles: ["Property List", "JSON"])
121+
popUpButton.selectItem(at: 0)
122+
popUpButton.stringValue = "File type"
123+
popUpButton.translatesAutoresizingMaskIntoConstraints = false
124+
accessoryView.addSubview(popUpButton)
125+
126+
let popUpButtonLabel = NSTextField(labelWithString: "File type")
127+
popUpButtonLabel.translatesAutoresizingMaskIntoConstraints = false
128+
accessoryView.addSubview(popUpButtonLabel)
129+
accessoryView.translatesAutoresizingMaskIntoConstraints = false
130+
131+
// popUpButtonLabel.leadingAnchor.constraint(greaterThanOrEqualTo: accessoryView.leadingAnchor, constant: 20.0).isActive = true
132+
popUpButtonLabel.trailingAnchor.constraint(equalTo: popUpButton.leadingAnchor, constant: -8.0).isActive = true
133+
popUpButtonLabel.trailingAnchor.constraint(lessThanOrEqualTo: accessoryView.centerXAnchor, constant: 0).isActive = true
134+
popUpButtonLabel.centerYAnchor.constraint(equalTo: popUpButton.centerYAnchor, constant: 0).isActive = true
135+
// popUpButton.trailingAnchor.constraint(lessThanOrEqualTo: accessoryView.trailingAnchor, constant: -20.0).isActive = true
136+
popUpButton.leadingAnchor.constraint(lessThanOrEqualTo: accessoryView.centerXAnchor, constant: 0).isActive = true
137+
popUpButton.topAnchor.constraint(equalTo: accessoryView.topAnchor, constant: 8.0).isActive = true
138+
popUpButton.bottomAnchor.constraint(equalTo: accessoryView.bottomAnchor, constant: -8.0).isActive = true
139+
popUpButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 20.0).isActive = true
140+
popUpButton.widthAnchor.constraint(lessThanOrEqualToConstant: 200.0).isActive = true
141+
142+
savePanel.accessoryView = accessoryView
143+
self.savePanel = savePanel
113144

114145
let result = savePanel.runModal()
115146

116147
if result == .OK,
117-
let url = savePanel.url
148+
var url = savePanel.url
118149
{
150+
let selectedItemIndex = popUpButton.indexOfSelectedItem
151+
119152
// Store the accessory file
120-
if url.pathExtension == "plist" {
153+
if selectedItemIndex == 0 {
154+
if url.pathExtension != "plist" {
155+
url = url.appendingPathExtension("plist")
156+
}
121157
let propertyList = try PropertyListEncoder().encode(accessories)
122158
try propertyList.write(to: url)
123-
}else if url.pathExtension == "json" {
159+
}else if selectedItemIndex == 1 {
160+
if url.pathExtension != "json" {
161+
url = url.appendingPathExtension("json")
162+
}
124163
let jsonObject = try JSONEncoder().encode(accessories)
125164
try jsonObject.write(to: url)
126165
}
@@ -129,11 +168,32 @@ class AccessoryController: ObservableObject {
129168
}
130169
throw ImportError.cancelled
131170
}
171+
172+
@objc func exportFileTypeChanged(button: NSPopUpButton) {
173+
if button.indexOfSelectedItem == 0 {
174+
if #available(macOS 12.0, *) {
175+
self.savePanel?.allowedContentTypes = [.propertyList]
176+
}else {
177+
self.savePanel?.allowedFileTypes = ["plist"]
178+
}
179+
}else {
180+
if #available(macOS 12.0, *) {
181+
self.savePanel?.allowedContentTypes = [.json]
182+
}else {
183+
self.savePanel?.allowedFileTypes = ["json"]
184+
}
185+
}
186+
}
132187

133188
/// Let the user select a file to import the accessories exported by another OpenHaystack instance.
134189
func importAccessories() throws {
135190
let openPanel = NSOpenPanel()
136-
openPanel.allowedFileTypes = ["plist"]
191+
if #available(macOS 12.0, *) {
192+
openPanel.allowedContentTypes = [.json, .propertyList]
193+
}else {
194+
openPanel.allowedFileTypes = ["json", "plist"]
195+
}
196+
137197
openPanel.canCreateDirectories = true
138198
openPanel.directoryURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
139199
openPanel.message = "Import an accessories file that includes the private keys"
@@ -144,9 +204,14 @@ class AccessoryController: ObservableObject {
144204
if result == .OK,
145205
let url = openPanel.url
146206
{
147-
let propertyList = try Data(contentsOf: url)
148-
var importedAccessories = try PropertyListDecoder().decode([Accessory].self, from: propertyList)
149-
207+
let accessoryData = try Data(contentsOf: url)
208+
var importedAccessories: [Accessory]
209+
if url.pathExtension == "plist" {
210+
importedAccessories = try PropertyListDecoder().decode([Accessory].self, from: accessoryData)
211+
}else {
212+
importedAccessories = try JSONDecoder().decode([Accessory].self, from: accessoryData)
213+
}
214+
150215
var updatedAccessories = self.accessories
151216
// Filter out accessories with the same id (no duplicates)
152217
importedAccessories = importedAccessories.filter({ acc in !self.accessories.contains(where: { acc.id == $0.id }) })

OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,10 @@ class Accessory: ObservableObject, Codable, Identifiable, Equatable, Hashable {
9090
self.name = try container.decode(String.self, forKey: .name)
9191
self.id = try container.decode(Int.self, forKey: .id)
9292
self.privateKey = try container.decode(Data.self, forKey: .privateKey)
93-
self.symmetricKey = (try? container.decode(Data.self, forKey: .symmetricKey)) ?? SymmetricKey(size: .bits256).withUnsafeBytes { return Data($0) }
93+
let symmetricKey = (try? container.decode(Data.self, forKey: .symmetricKey)) ?? SymmetricKey(size: .bits256).withUnsafeBytes { return Data($0) }
94+
self.symmetricKey = symmetricKey
9495
self.usesDerivation = (try? container.decode(Bool.self, forKey: .usesDerivation)) ?? false
95-
self.oldestRelevantSymmetricKey = (try? container.decode(Data.self, forKey: .oldestRelevantSymmetricKey)) ?? self.symmetricKey
96+
self.oldestRelevantSymmetricKey = (try? container.decode(Data.self, forKey: .oldestRelevantSymmetricKey)) ?? symmetricKey
9697
self.lastDerivationTimestamp = (try? container.decode(Date.self, forKey: .lastDerivationTimestamp)) ?? Date()
9798
self.updateInterval = (try? container.decode(TimeInterval.self, forKey: .updateInterval)) ?? TimeInterval(60 * 60 * 24)
9899
self.icon = (try? container.decode(String.self, forKey: .icon)) ?? ""

0 commit comments

Comments
 (0)