167 lines
5.5 KiB
Swift
167 lines
5.5 KiB
Swift
import SwiftUI
|
|
import HealthKit
|
|
import HealthKitExtensions
|
|
import HealthDB
|
|
import SFSafeSymbols
|
|
|
|
struct DatabasesTab: View {
|
|
|
|
@ObservedObject
|
|
var database: Database
|
|
|
|
@ObservedObject
|
|
var databases: DatabaseList
|
|
|
|
@State var navigationPath: NavigationPath = .init()
|
|
|
|
@State
|
|
private var showFailedToOpenAlert = false
|
|
|
|
@State
|
|
private var showDocumentPicker = false
|
|
|
|
@State
|
|
private var showOptionsForSelectedDatabase = false
|
|
|
|
@State
|
|
private var changedDatabaseName: String = ""
|
|
|
|
@State
|
|
private var showChangeDatabaseNameAlert = false
|
|
|
|
@State
|
|
private var selectedDatabase: DatabaseFile? = nil
|
|
|
|
private var openCloseButtonText: String {
|
|
database.file == selectedDatabase ? "Close" : "Open"
|
|
}
|
|
|
|
private var defaultSelectionButtonText: String {
|
|
(selectedDatabase?.isDefault ?? false) ? "Remove as default" : "Set as default"
|
|
}
|
|
|
|
private func symbol(for database: DatabaseFile) -> SFSymbol {
|
|
self.database.file == database ? .circleInsetFilled : .circle
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack(path: $navigationPath) {
|
|
List {
|
|
ForEach(databases.databases) { database in
|
|
HStack(alignment: .center) {
|
|
Image(systemSymbol: symbol(for: database))
|
|
VStack(alignment: .leading) {
|
|
HStack(alignment: .firstTextBaseline) {
|
|
Text(database.name)
|
|
.font(.headline)
|
|
if database.isDefault {
|
|
Text("(Default)")
|
|
.font(.caption)
|
|
}
|
|
}
|
|
Text(database.file)
|
|
.font(.caption)
|
|
}
|
|
}
|
|
.onTapGesture {
|
|
self.selectedDatabase = database
|
|
self.showOptionsForSelectedDatabase = true
|
|
}
|
|
}.onDelete(perform: databases.deleteDatabases)
|
|
}
|
|
.navigationTitle("Databases")
|
|
.toolbar {
|
|
Button(action: displayDocumentPicker) {
|
|
Label("Add", systemSymbol: .plus)
|
|
}
|
|
}
|
|
.fileImporter(isPresented: $showDocumentPicker,
|
|
allowedContentTypes: [.database],
|
|
onCompletion: handle)
|
|
.confirmationDialog("Change background", isPresented: $showOptionsForSelectedDatabase) {
|
|
Button(openCloseButtonText, action: openOrCloseSelectedDatabase)
|
|
Button("Rename", action: renameSelectedDatabase)
|
|
Button(defaultSelectionButtonText, action: toggleDefaultForSelectedDatabase)
|
|
Button("Cancel", role: .cancel) { }
|
|
} message: {
|
|
Text("Select a new color")
|
|
}
|
|
.alert("Update name", isPresented: $showChangeDatabaseNameAlert, actions: {
|
|
TextField("Name", text: $changedDatabaseName)
|
|
Button("Update", action: saveNewNameForSelectedDatabase)
|
|
Button("Cancel", role: .cancel, action: {})
|
|
}, message: {
|
|
Text("Please enter the new name for the database")
|
|
})
|
|
.alert("Failed to open", isPresented: $showFailedToOpenAlert) {
|
|
Button("Dismiss", role: .cancel, action: {})
|
|
} message: {
|
|
Text("The selected database could not be opened. Make sure that it is a valid Health database.")
|
|
}
|
|
|
|
}.onAppear(perform: databases.load)
|
|
}
|
|
|
|
private func openOrCloseSelectedDatabase() {
|
|
guard let selectedDatabase else { return }
|
|
defer { self.selectedDatabase = nil }
|
|
|
|
guard database.load(database: selectedDatabase) else {
|
|
return
|
|
}
|
|
}
|
|
|
|
private func renameSelectedDatabase() {
|
|
guard let selectedDatabase else { return }
|
|
self.changedDatabaseName = selectedDatabase.name
|
|
self.showChangeDatabaseNameAlert = true
|
|
}
|
|
|
|
private func saveNewNameForSelectedDatabase() {
|
|
defer { changedDatabaseName = "" }
|
|
guard let selectedDatabase else { return }
|
|
guard let updated = databases.update(name: changedDatabaseName, for: selectedDatabase) else {
|
|
return
|
|
}
|
|
if database.file == selectedDatabase {
|
|
// Update open database file
|
|
database.file = updated
|
|
}
|
|
}
|
|
|
|
private func toggleDefaultForSelectedDatabase() {
|
|
guard let selectedDatabase else { return }
|
|
defer { self.selectedDatabase = nil }
|
|
databases.setAsDefault(database: selectedDatabase)
|
|
}
|
|
|
|
private func displayDocumentPicker() {
|
|
showDocumentPicker = true
|
|
}
|
|
|
|
private func handle(result: Result<URL, Error>) {
|
|
do {
|
|
let selectedFile: URL = try result.get()
|
|
if selectedFile.startAccessingSecurityScopedResource() {
|
|
defer { selectedFile.stopAccessingSecurityScopedResource() }
|
|
databases.importDatabase(at: selectedFile)
|
|
} else {
|
|
print("No access to file")
|
|
}
|
|
} catch {
|
|
print("No file selected: \(error)")
|
|
}
|
|
}
|
|
|
|
private func handlePicked(urls: [URL]) {
|
|
print("Files picked.")
|
|
for url in urls {
|
|
databases.importDatabase(at: url)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
DatabasesTab(database: Database(), databases: .init())
|
|
}
|