Lots more SwiftUI cleanup.

This commit is contained in:
Max Goedjen 2020-09-10 23:17:03 -07:00
parent c2fc71d299
commit 54d7fbe88d
No known key found for this signature in database
GPG Key ID: E58C21DD77B9B8E8
6 changed files with 82 additions and 86 deletions

View File

@ -21,14 +21,14 @@ struct AppDelegate: App {
@SceneBuilder var body: some Scene { @SceneBuilder var body: some Scene {
WindowGroup { WindowGroup {
ContentView<Updater, AgentStatusChecker>() ContentView<Updater, AgentStatusChecker>(runningSetup: $showingSetup)
.environmentObject(storeList) .environmentObject(storeList)
.environmentObject(updater) .environmentObject(updater)
.environmentObject(agentStatusChecker) .environmentObject(agentStatusChecker)
.sheet(isPresented: $showingSetup) { .sheet(isPresented: $showingSetup) {
SetupView { completed in SetupView { completed in
self.showingSetup = false showingSetup = false
self.hasRunSetup = completed hasRunSetup = completed
} }
} }
.onAppear { .onAppear {
@ -51,7 +51,7 @@ struct AppDelegate: App {
} }
CommandGroup(after: .help) { CommandGroup(after: .help) {
Button("Setup Secret Agent") { Button("Setup Secret Agent") {
self.showingSetup = true showingSetup = true
} }
} }
} }

View File

@ -7,39 +7,40 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
@EnvironmentObject var storeList: SecretStoreList @EnvironmentObject var storeList: SecretStoreList
@EnvironmentObject var updater: UpdaterType @EnvironmentObject var updater: UpdaterType
@EnvironmentObject var agentStatusChecker: AgentStatusCheckerType @EnvironmentObject var agentStatusChecker: AgentStatusCheckerType
var runSetupBlock: (() -> Void)?
@State private var active: AnySecret.ID? @State private var active: AnySecret.ID?
@State private var showingCreation = false @State private var showingCreation = false
@State private var deletingSecret: AnySecret? @State private var deletingSecret: AnySecret?
@State private var selectedUpdate: Release? @State private var selectedUpdate: Release?
@Binding var runningSetup: Bool
var body: some View { var body: some View {
VStack { VStack {
if storeList.anyAvailable { if storeList.anyAvailable {
NavigationView { NavigationView {
List(selection: $active) { List(selection: $active) {
ForEach(storeList.stores) { store in ForEach(storeList.stores) { store in
if store.isAvailable { if store.isAvailable {
Section(header: Text(store.name)) { Section(header: Text(store.name)) {
if store.secrets.isEmpty { if store.secrets.isEmpty {
if store is AnySecretStoreModifiable { if store is AnySecretStoreModifiable {
NavigationLink(destination: EmptyStoreModifiableView(), tag: Constants.emptyStoreModifiableTag, selection: self.$active) { NavigationLink(destination: EmptyStoreModifiableView(), tag: Constants.emptyStoreModifiableTag, selection: $active) {
Text("No Secrets") Text("No Secrets")
}
} else {
NavigationLink(destination: EmptyStoreView(), tag: Constants.emptyStoreTag, selection: $active) {
Text("No Secrets")
}
} }
} else { } else {
NavigationLink(destination: EmptyStoreView(), tag: Constants.emptyStoreTag, selection: self.$active) { ForEach(store.secrets) { secret in
Text("No Secrets") NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: $active) {
} Text(secret.name)
} }.contextMenu {
} else { if store is AnySecretStoreModifiable {
ForEach(store.secrets) { secret in Button(action: { delete(secret: secret) }) {
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) { Text("Delete")
Text(secret.name) }
}.contextMenu {
if store is AnySecretStoreModifiable {
Button(action: { self.delete(secret: secret) }) {
Text("Delete")
} }
} }
} }
@ -47,42 +48,35 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
} }
} }
} }
}.onAppear {
active = nextDefaultSecret
} }
}.onAppear { .frame(minWidth: 100, idealWidth: 240)
self.active = self.nextDefaultSecret .sheet(item: $deletingSecret) { secret in
} if storeList.modifiableStore != nil {
.frame(minWidth: 100, idealWidth: 240) DeleteSecretView(secret: secret, store: storeList.modifiableStore!) { deleted in
.sheet(item: $deletingSecret) { secret in deletingSecret = nil
if self.storeList.modifiableStore != nil { if deleted {
DeleteSecretView(secret: secret, store: self.storeList.modifiableStore!) { deleted in active = nextDefaultSecret
self.deletingSecret = nil }
if deleted {
self.active = self.nextDefaultSecret
} }
} }
} }
} }
}
} else { } else {
NoStoresView() NoStoresView()
} }
} }
.sheet(isPresented: $showingCreation) { .sheet(isPresented: $showingCreation) {
CreateSecretView { CreateSecretView(showing: $showingCreation)
self.showingCreation = false
}
} }
.frame(minWidth: 640, minHeight: 320) .frame(minWidth: 640, minHeight: 320)
.toolbar { .toolbar {
// if updater.update != nil { updateNotice
updateNotice() agentNotice
// }
// if !agentStatusChecker.running {
// agentNotice()z
// }
ToolbarItem { ToolbarItem {
Button(action: { Button(action: {
self.showingCreation = true showingCreation = true
}, label: { }, label: {
Image(systemName: "plus") Image(systemName: "plus")
}) })
@ -90,12 +84,9 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
} }
} }
func updateNotice() -> ToolbarItem<Void, AnyView> { var updateNotice: ToolbarItem<Void, AnyView> {
// let update = updater.update ?? Release(name: "", html_url: URL(string:"https://example.com")!, body: "")
guard let update = updater.update else { guard let update = updater.update else {
return ToolbarItem { return ToolbarItem { AnyView(Spacer()) }
AnyView(Spacer())
}
} }
let color: Color let color: Color
let text: String let text: String
@ -108,7 +99,7 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
} }
return ToolbarItem { return ToolbarItem {
AnyView(Button(action: { AnyView(Button(action: {
self.selectedUpdate = update selectedUpdate = update
}, label: { }, label: {
Text(text) Text(text)
.font(.headline) .font(.headline)
@ -122,20 +113,25 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
) )
} }
} }
//
// func agentNotice() -> ToolbarItem<Void, AnyView> { var agentNotice: ToolbarItem<Void, AnyView> {
// ToolbarItem { guard agentStatusChecker.running else {
// Button(action: { return ToolbarItem { AnyView(Spacer()) }
// self.runSetupBlock?() }
// }, label: { return ToolbarItem {
// Text("Agent is not running.") AnyView(
// .font(.headline) Button(action: {
// .foregroundColor(.white) runningSetup = true
// }) }, label: {
// .background(Color.orange) Text("Secret Agent Is Not Running")
// .cornerRadius(5) .font(.headline)
// } .foregroundColor(.white)
// } })
.background(Color.orange)
.cornerRadius(5)
)
}
}
func delete<SecretType: Secret>(secret: SecretType) { func delete<SecretType: Secret>(secret: SecretType) {
deletingSecret = AnySecret(secret) deletingSecret = AnySecret(secret)
@ -143,12 +139,12 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
var nextDefaultSecret: AnyHashable? { var nextDefaultSecret: AnyHashable? {
let fallback: AnyHashable let fallback: AnyHashable
if self.storeList.modifiableStore?.isAvailable ?? false { if storeList.modifiableStore?.isAvailable ?? false {
fallback = Constants.emptyStoreModifiableTag fallback = Constants.emptyStoreModifiableTag
} else { } else {
fallback = Constants.emptyStoreTag fallback = Constants.emptyStoreTag
} }
return self.storeList.stores.compactMap(\.secrets.first).first?.id ?? fallback return storeList.stores.compactMap(\.secrets.first).first?.id ?? fallback
} }
} }

View File

@ -7,9 +7,8 @@ struct CreateSecretView: View {
@State var name = "" @State var name = ""
@State var requiresAuthentication = true @State var requiresAuthentication = true
@Binding var showing: Bool
var dismissalBlock: () -> ()
var body: some View { var body: some View {
VStack { VStack {
HStack { HStack {
@ -36,8 +35,10 @@ struct CreateSecretView: View {
} }
HStack { HStack {
Spacer() Spacer()
Button("Cancel", action: dismissalBlock) Button("Cancel") {
.keyboardShortcut(.cancelAction) showing = false
}
.keyboardShortcut(.cancelAction)
Button("Create", action: save) Button("Create", action: save)
.disabled(name.isEmpty) .disabled(name.isEmpty)
.keyboardShortcut(.defaultAction) .keyboardShortcut(.defaultAction)
@ -47,6 +48,6 @@ struct CreateSecretView: View {
func save() { func save() {
try! store.create(name: name, requiresAuthentication: requiresAuthentication) try! store.create(name: name, requiresAuthentication: requiresAuthentication)
dismissalBlock() showing = false
} }
} }

View File

@ -38,7 +38,7 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
} }
} }
.onExitCommand { .onExitCommand {
self.dismissalBlock(false) dismissalBlock(false)
} }
} }
HStack { HStack {
@ -47,7 +47,7 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
.disabled(confirm != secret.name) .disabled(confirm != secret.name)
.keyboardShortcut(.delete) .keyboardShortcut(.delete)
Button("Don't Delete") { Button("Don't Delete") {
self.dismissalBlock(false) dismissalBlock(false)
} }
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
} }
@ -58,6 +58,6 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
func delete() { func delete() {
try! store.delete(secret: secret) try! store.delete(secret: secret)
self.dismissalBlock(true) dismissalBlock(true)
} }
} }

View File

@ -17,7 +17,7 @@ struct SecretDetailView<SecretType: Secret>: View {
.frame(minWidth: 150, maxWidth: .infinity) .frame(minWidth: 150, maxWidth: .infinity)
.padding() .padding()
}.onDrag { }.onDrag {
return NSItemProvider(item: NSData(data: self.keyWriter.openSSHFingerprint(secret: self.secret).data(using: .utf8)!), typeIdentifier: kUTTypeUTF8PlainText as String) return NSItemProvider(item: NSData(data: keyWriter.openSSHFingerprint(secret: secret).data(using: .utf8)!), typeIdentifier: kUTTypeUTF8PlainText as String)
} }
Spacer().frame(height: 10) Spacer().frame(height: 10)
GroupBox(label: Text("Public Key")) { GroupBox(label: Text("Public Key")) {
@ -35,7 +35,7 @@ struct SecretDetailView<SecretType: Secret>: View {
.padding() .padding()
} }
.onDrag { .onDrag {
return NSItemProvider(item: NSData(data: self.keyString.data(using: .utf8)!), typeIdentifier: kUTTypeUTF8PlainText as String) return NSItemProvider(item: NSData(data: keyString.data(using: .utf8)!), typeIdentifier: kUTTypeUTF8PlainText as String)
} }
Spacer() Spacer()
} }

View File

@ -11,17 +11,17 @@ struct SetupView: View {
index: 1, index: 1,
nestedView: nil, nestedView: nil,
actionText: "Install") { actionText: "Install") {
self.installLaunchAgent() installLaunchAgent()
} }
SetupStepView(text: "Add this line to your shell config (.bashrc or .zshrc) telling SSH to talk to SecretAgent when it wants to authenticate. Drag this into your config file.", SetupStepView(text: "Add this line to your shell config (.bashrc or .zshrc) telling SSH to talk to SecretAgent when it wants to authenticate. Drag this into your config file.",
index: 2, index: 2,
nestedView: SetupStepCommandView(text: Constants.socketPrompt), nestedView: SetupStepCommandView(text: Constants.socketPrompt),
actionText: "Added") { actionText: "Added") {
self.markAsDone() markAsDone()
} }
HStack { HStack {
Spacer() Spacer()
Button(action: { self.completion?(true) }) { Button(action: { completion?(true) }) {
Text("Finish") Text("Finish")
} }
.padding() .padding()
@ -69,7 +69,7 @@ struct SetupStepView<NestedViewType: View>: View {
} }
.padding() .padding()
Button(action: { Button(action: {
self.completed = self.action() completed = action()
}) { }) {
Text(actionText) Text(actionText)
}.disabled(completed) }.disabled(completed)
@ -101,8 +101,7 @@ struct SetupStepCommandView: View {
.background(Color(white: 0, opacity: 0.10)) .background(Color(white: 0, opacity: 0.10))
.cornerRadius(10) .cornerRadius(10)
.onDrag { .onDrag {
return NSItemProvider(item: NSData(data: self.text.data(using: .utf8)!), typeIdentifier: kUTTypeUTF8PlainText as String) return NSItemProvider(item: NSData(data: text.data(using: .utf8)!), typeIdentifier: kUTTypeUTF8PlainText as String)
} }
} }