diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index ab94fb6..bd45ff3 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 50A5C18F240E4B4C00E2996C /* SecretKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; }; 50A5C190240E4B4C00E2996C /* SecretKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 50617DA823FCE4AB0099B055 /* SecretKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; }; + 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; }; 50C385A3240789E600AF2719 /* OpenSSHReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A2240789E600AF2719 /* OpenSSHReader.swift */; }; 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; }; 50C385A9240B636500AF2719 /* SetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A8240B636500AF2719 /* SetupView.swift */; }; @@ -199,6 +200,7 @@ 50A3B79D24026B9900D209EA /* SocketController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketController.swift; sourceTree = ""; }; 50A3B79F24026B9900D209EA /* Agent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Agent.swift; sourceTree = ""; }; 50B8550C24138C4F009958AC /* DeleteSecretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeleteSecretView.swift; sourceTree = ""; }; + 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStoreView.swift; sourceTree = ""; }; 50C385A2240789E600AF2719 /* OpenSSHReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OpenSSHReader.swift; path = SecretKit/Common/OpenSSH/OpenSSHReader.swift; sourceTree = SOURCE_ROOT; }; 50C385A42407A76D00AF2719 /* SecretDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretDetailView.swift; sourceTree = ""; }; 50C385A8240B636500AF2719 /* SetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupView.swift; sourceTree = ""; }; @@ -299,6 +301,7 @@ 50C385A42407A76D00AF2719 /* SecretDetailView.swift */, 5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */, 50B8550C24138C4F009958AC /* DeleteSecretView.swift */, + 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */, 50C385A8240B636500AF2719 /* SetupView.swift */, 50617D8623FCE48E0099B055 /* Assets.xcassets */, 50617D8B23FCE48E0099B055 /* Main.storyboard */, @@ -733,6 +736,7 @@ 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */, 5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */, 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */, + 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50617D8323FCE48E0099B055 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Secretive/ContentView.swift b/Secretive/ContentView.swift index 951dfa6..9d6159b 100644 --- a/Secretive/ContentView.swift +++ b/Secretive/ContentView.swift @@ -4,10 +4,10 @@ import SecretKit struct ContentView: View { @ObservedObject var storeList: SecretStoreList - @State var active: AnySecret.ID? - - @State var showingDeletion = false - @State var deletingSecret: AnySecret? + + @State fileprivate var active: AnySecret.ID? + @State fileprivate var showingDeletion = false + @State fileprivate var deletingSecret: AnySecret? var body: some View { NavigationView { @@ -15,13 +15,25 @@ struct ContentView: View { ForEach(storeList.stores) { store in if store.isAvailable { Section(header: Text(store.name)) { - ForEach(store.secrets) { secret in - NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { - Text(secret.name) - }.contextMenu { - if store is AnySecretStoreModifiable { - Button(action: { self.delete(secret: secret) }) { - Text("Delete") + if store.secrets.isEmpty { + if store is AnySecretStoreModifiable { + NavigationLink(destination: EmptyStoreModifiableView()) { + Text("No Secrets") + } + } else { + NavigationLink(destination: EmptyStoreView()) { + Text("No Secrets") + } + } + } else { + ForEach(store.secrets) { secret in + NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { + Text(secret.name) + }.contextMenu { + if store is AnySecretStoreModifiable { + Button(action: { self.delete(secret: secret) }) { + Text("Delete") + } } } } @@ -53,9 +65,14 @@ struct ContentView: View { } } -// -//struct ContentView_Previews: PreviewProvider { -// static var previews: some View { -// ContentView(store: Preview.Store(numberOfRandomSecrets: 10)) -// } -//} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + Group { + ContentView(storeList: Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)])) + ContentView(storeList: Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()])) + ContentView(storeList: Preview.storeList(stores: [Preview.Store()])) + ContentView(storeList: Preview.storeList(modifiableStores: [Preview.StoreModifiable()])) + } + } +} diff --git a/Secretive/EmptyStoreView.swift b/Secretive/EmptyStoreView.swift new file mode 100644 index 0000000..94d59e4 --- /dev/null +++ b/Secretive/EmptyStoreView.swift @@ -0,0 +1,53 @@ +import SwiftUI + +struct EmptyStoreView: View { + + var body: some View { + VStack { + Text("No Secrets").bold() + Text("Use your Smart Card's management tool to create a secret.") + Text("Secretive only supports Elliptic Curve keys.") + }.frame(maxWidth: .infinity, maxHeight: .infinity) + } + +} + +struct EmptyStoreModifiableView: View { + + var body: some View { + GeometryReader { windowGeometry in + VStack { + GeometryReader { g in + Path { path in + path.move(to: CGPoint(x: g.size.width / 2, y: g.size.height)) + path.addCurve(to: + CGPoint(x: g.size.width * (3/4), y: g.size.height * (1/2)), control1: + CGPoint(x: g.size.width / 2, y: g.size.height * (1/2)), control2: + CGPoint(x: g.size.width * (3/4), y: g.size.height * (1/2))) + path.addCurve(to: + CGPoint(x: g.size.width, y: 0), control1: + CGPoint(x: g.size.width, y: g.size.height * (1/2)), control2: + CGPoint(x: g.size.width, y: 0)) + }.stroke(style: StrokeStyle(lineWidth: 5, lineCap: .round)) + Path { path in + path.move(to: CGPoint(x: g.size.width - 10, y: 0)) + path.addLine(to: CGPoint(x: g.size.width, y: -10)) + path.addLine(to: CGPoint(x: g.size.width + 10, y: 0)) + }.fill() + }.frame(height: (windowGeometry.size.height/2) - 20).padding() + Text("No Secrets").bold() + Text("Create a new one by clicking here.") + Spacer() + }.frame(maxWidth: .infinity, maxHeight: .infinity) + } + } +} + +struct EmptyStoreModifiableView_Previews: PreviewProvider { + static var previews: some View { + Group { + EmptyStoreView() + EmptyStoreModifiableView() + } + } +} diff --git a/Secretive/Preview Content/PreviewStore.swift b/Secretive/Preview Content/PreviewStore.swift index db9980a..892eaaa 100644 --- a/Secretive/Preview Content/PreviewStore.swift +++ b/Secretive/Preview Content/PreviewStore.swift @@ -23,15 +23,15 @@ extension Preview { let isAvailable = true let id = UUID() - let name = "Preview Store" + var name: String { "Preview Store" } @Published var secrets: [Secret] = [] init(secrets: [Secret]) { self.secrets.append(contentsOf: secrets) } - init(numberOfRandomSecrets: Int) { - let new = (0...numberOfRandomSecrets).map { Secret(name: String(describing: $0)) } + init(numberOfRandomSecrets: Int = 5) { + let new = (0.. SecretStoreList { + let list = SecretStoreList() + for store in stores { + list.add(store: store) + } + for storeModifiable in modifiableStores { + list.add(store: storeModifiable) + } + return list } }