OTP/OTP/Views/KeysSection.swift

142 lines
4.7 KiB
Swift

//
// KeysSection.swift
// OTP
//
// Created by Shadowfacts on 8/24/21.
//
import SwiftUI
import UniformTypeIdentifiers
struct KeysSection: View {
@ObservedObject private var store: KeyStore = .shared
@ObservedObject private var entryHolder: AppView.CodeHolder
@State private var editedEntry: AppView.CodeEntry? = nil
@State private var presentedQRCode: AppView.CodeEntry? = nil
init(codeHolder: AppView.CodeHolder) {
self.entryHolder = codeHolder
}
var body: some View {
Section {
ForEach(entryHolder.sortedEntries) { (entry) in
KeyView(key: entry.key, currentCode: entry.code)
// disabled because dropping onto list rows does not work :/
// .onDrag {
// NSItemProvider(object: entry.id.uuidString as NSString)
// }
.contextMenu {
self.keyMenu(entry: entry)
}
}
.onDelete { (indices) in
withAnimation(.default) {
for index in indices {
store.removeKey(entryID: entryHolder.sortedEntries[index].id)
}
}
}
}
.sheet(item: $editedEntry, content: self.editFormSheet)
.sheet(item: $presentedQRCode, content: self.qrCodeSheet)
}
@ViewBuilder
private func keyMenu(entry: AppView.CodeEntry) -> some View {
Section {
Menu {
Button("No Folder") {
withAnimation {
store.moveEntryToFolder(entryID: entry.id, folderID: nil)
}
}
.disabled(entry.entry.folderID == nil)
Section {
ForEach(store.sortedFolders) { (folder) in
Button {
withAnimation {
store.moveEntryToFolder(entryID: entry.id, folderID: folder.id)
}
} label: {
Text(folder.name)
}
.disabled(entry.entry.folderID == folder.id)
}
}
} label: {
Label("Move to Folder", systemImage: "folder")
}
Button {
// even the .contextMenu closure is called again, SwiftUI seems not to update the actual context menu buttons
// so when this closure is called, it's copy of entry may be stale if the entry's since been edited
// so we lookup the known current one by ID and edit that
let realEntry = entryHolder.entries.first { $0.id == entry.id }
editedEntry = realEntry
} label: {
Label("Edit Key", systemImage: "pencil")
}
// todo: can't mark menu as destructive
Menu {
Button("Cancel", role: .cancel) {}
Button("Delete Key", role: .destructive) {
// todo: why doesn't this animation work?
withAnimation(.default) {
store.removeKey(entryID: entry.id)
}
}
} label: {
Label("Delete Key", systemImage: "trash")
}
}
Section {
Button {
let realEntry = entryHolder.entries.first { $0.id == entry.id }
presentedQRCode = realEntry
} label: {
Label("Export as QR", systemImage: "qrcode")
}
Button {
UIPasteboard.general.url = entry.key.url
} label: {
Label("Copy as URL", systemImage: "link")
}
}
}
private func editFormSheet(editedEntry: AppView.CodeEntry) -> some View {
NavigationView {
EditKeyForm(editingKey: editedEntry.key) { (action) in
self.editedEntry = nil
switch action {
case .cancel:
break
case .save(let key):
store.updateKey(entryID: editedEntry.id, newKey: key)
}
}
.navigationTitle("Edit Key")
}
}
private func qrCodeSheet(entry: AppView.CodeEntry) -> some View {
NavigationView {
QRCodeView(key: entry.key)
.id(entry.id)
}
}
}
struct KeysSection_Previews: PreviewProvider {
static var previews: some View {
KeysSection(codeHolder: .init(store: .shared))
}
}