diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index 38280a8..1701804 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; }; 50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; }; + 50153E22250DECA300525160 /* SecretListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListView.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; 50524B442420969E008DBD97 /* OpenSSHWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */; }; 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; }; @@ -41,6 +42,7 @@ 506838A12415EA5600F55094 /* AnySecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506838A02415EA5600F55094 /* AnySecret.swift */; }; 506838A32415EA5D00F55094 /* AnySecretStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506838A22415EA5D00F55094 /* AnySecretStore.swift */; }; 506AB87E2412334700335D91 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; }; 507CE4ED2420A3C70029F750 /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79F24026B9900D209EA /* Agent.swift */; }; 507CE4EE2420A3CA0029F750 /* SocketController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79D24026B9900D209EA /* SocketController.swift */; }; 507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4EF2420A4C50029F750 /* SigningWitness.swift */; }; @@ -206,6 +208,7 @@ /* Begin PBXFileReference section */ 50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = ""; }; + 50153E21250DECA300525160 /* SecretListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListView.swift; sourceTree = ""; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; }; 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSSHWriterTests.swift; sourceTree = ""; }; 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = ""; }; @@ -242,6 +245,7 @@ 5068389D241471CD00F55094 /* SecretStoreList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretStoreList.swift; sourceTree = ""; }; 506838A02415EA5600F55094 /* AnySecret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnySecret.swift; sourceTree = ""; }; 506838A22415EA5D00F55094 /* AnySecretStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnySecretStore.swift; sourceTree = ""; }; + 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = ""; }; 507CE4EF2420A4C50029F750 /* SigningWitness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningWitness.swift; sourceTree = ""; }; 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestProvenance.swift; sourceTree = ""; }; 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestTracer.swift; sourceTree = ""; }; @@ -498,6 +502,8 @@ isa = PBXGroup; children = ( 50617D8423FCE48E0099B055 /* ContentView.swift */, + 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */, + 50153E21250DECA300525160 /* SecretListView.swift */, 50C385A42407A76D00AF2719 /* SecretDetailView.swift */, 5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */, 50B8550C24138C4F009958AC /* DeleteSecretView.swift */, @@ -919,6 +925,7 @@ 50C385A9240B636500AF2719 /* SetupView.swift in Sources */, 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */, 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */, + 5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */, 50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */, 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */, 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */, @@ -929,6 +936,7 @@ 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50617D8323FCE48E0099B055 /* App.swift in Sources */, 506772C92425BB8500034DED /* NoStoresView.swift in Sources */, + 50153E22250DECA300525160 /* SecretListView.swift in Sources */, 508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */, 508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */, ); diff --git a/Secretive/Views/ContentView.swift b/Secretive/Views/ContentView.swift index d13b5da..43d95e0 100644 --- a/Secretive/Views/ContentView.swift +++ b/Secretive/Views/ContentView.swift @@ -8,66 +8,19 @@ struct ContentView(secret: SecretType) { - deletingSecret = AnySecret(secret) - } - - var nextDefaultSecret: AnyHashable? { - let fallback: AnyHashable - if storeList.modifiableStore?.isAvailable ?? false { - fallback = Constants.emptyStoreModifiableTag - } else { - fallback = Constants.emptyStoreTag - } - return storeList.stores.compactMap(\.secrets.first).first?.id ?? fallback - } } -private enum Constants { - static let emptyStoreModifiableTag: AnyHashable = "emptyStoreModifiableTag" - static let emptyStoreTag: AnyHashable = "emptyStoreModifiableTag" -} - // //#if DEBUG // diff --git a/Secretive/Views/SecretListView.swift b/Secretive/Views/SecretListView.swift new file mode 100644 index 0000000..6b3ccdb --- /dev/null +++ b/Secretive/Views/SecretListView.swift @@ -0,0 +1,40 @@ +import SwiftUI +import SecretKit + +struct SecretListView: View { + + @ObservedObject var store: AnySecretStore + @Binding var activeSecret: AnySecret.ID? + @Binding var deletingSecret: AnySecret? + + var deletedSecret: (AnySecret) -> Void + + var body: some View { + ForEach(store.secrets) { secret in + NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: $activeSecret) { + Text(secret.name) + }.contextMenu { + if store is AnySecretStoreModifiable { + Button(action: { delete(secret: secret) }) { + Text("Delete") + } + } + } + .sheet(item: $deletingSecret) { secret in + if let modifiable = store as? AnySecretStoreModifiable { + DeleteSecretView(secret: secret, store: modifiable) { deleted in + deletingSecret = nil + if deleted { + deletedSecret(AnySecret(secret)) + } + } + } + } + } + } + + func delete(secret: SecretType) { + deletingSecret = AnySecret(secret) + } + +} diff --git a/Secretive/Views/StoreListView.swift b/Secretive/Views/StoreListView.swift new file mode 100644 index 0000000..35d87fd --- /dev/null +++ b/Secretive/Views/StoreListView.swift @@ -0,0 +1,63 @@ +import SwiftUI +import SecretKit + +struct StoreListView: View { + + @State private var activeSecret: AnySecret.ID? + @State private var deletingSecret: AnySecret? + + @EnvironmentObject var storeList: SecretStoreList + + var body: some View { + NavigationView { + List(selection: $activeSecret) { + ForEach(storeList.stores) { store in + if store.isAvailable { + Section(header: Text(store.name)) { + if store.secrets.isEmpty { + if store is AnySecretStoreModifiable { + NavigationLink(destination: EmptyStoreModifiableView(), tag: Constants.emptyStoreModifiableTag, selection: $activeSecret) { + Text("No Secrets") + } + } else { + NavigationLink(destination: EmptyStoreView(), tag: Constants.emptyStoreTag, selection: $activeSecret) { + Text("No Secrets") + } + } + } else { + SecretListView(store: store, activeSecret: $activeSecret, deletingSecret: $deletingSecret, deletedSecret: { _ in + activeSecret = nextDefaultSecret + }) + } + } + } + } + } + .listStyle(SidebarListStyle()) + .onAppear { + activeSecret = nextDefaultSecret + } + .frame(minWidth: 100, idealWidth: 240) + } + } + +} + +extension StoreListView { + + var nextDefaultSecret: AnyHashable? { + let fallback: AnyHashable + if storeList.modifiableStore?.isAvailable ?? false { + fallback = Constants.emptyStoreModifiableTag + } else { + fallback = Constants.emptyStoreTag + } + return storeList.stores.compactMap(\.secrets.first).first?.id ?? fallback + } + +} + +private enum Constants { + static let emptyStoreModifiableTag: AnyHashable = "emptyStoreModifiableTag" + static let emptyStoreTag: AnyHashable = "emptyStoreModifiableTag" +}