// // 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)) } }