Add option to rename keys/secrets (#216)

* Add option to rename secrets

* Address PR comments

Co-authored-by: Max Goedjen <max.goedjen@gmail.com>
This commit is contained in:
Dylan Lundy
2021-06-01 15:50:38 +09:30
committed by GitHub
parent cd965b9ec6
commit 8114acf50a
10 changed files with 159 additions and 57 deletions

View File

@@ -42,7 +42,6 @@ extension Preview {
}
class StoreModifiable: Store, SecretStoreModifiable {
override var name: String { "Modifiable Preview Store" }
func create(name: String, requiresAuthentication: Bool) throws {
@@ -50,8 +49,10 @@ extension Preview {
func delete(secret: Preview.Secret) throws {
}
}
func update(secret: Preview.Secret, name: String) throws {
}
}
}
extension Preview {

View File

@@ -30,9 +30,6 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
TextField(secret.name, text: $confirm)
}
}
.onExitCommand {
dismissalBlock(false)
}
}
HStack {
Spacer()
@@ -47,6 +44,9 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
}
.padding()
.frame(minWidth: 400)
.onExitCommand {
dismissalBlock(false)
}
}
func delete() {

View File

@@ -0,0 +1,50 @@
import SwiftUI
import SecretKit
struct RenameSecretView<StoreType: SecretStoreModifiable>: View {
@ObservedObject var store: StoreType
let secret: StoreType.SecretType
var dismissalBlock: (_ renamed: Bool) -> ()
@State private var newName = ""
var body: some View {
VStack {
HStack {
Image(nsImage: NSApp.applicationIconImage)
.resizable()
.frame(width: 64, height: 64)
.padding()
VStack {
HStack {
Text("Type your new name for \"\(secret.name)\" below.")
Spacer()
}
HStack {
TextField(secret.name, text: $newName).focusable()
}
}
}
HStack {
Spacer()
Button("Rename", action: rename)
.disabled(newName.count == 0)
.keyboardShortcut(.return)
Button("Cancel") {
dismissalBlock(false)
}.keyboardShortcut(.cancelAction)
}
}
.padding()
.frame(minWidth: 400)
.onExitCommand {
dismissalBlock(false)
}
}
func rename() {
try? store.update(secret: secret, name: newName)
dismissalBlock(true)
}
}

View File

@@ -0,0 +1,54 @@
import SwiftUI
import SecretKit
struct SecretListItemView: View {
@ObservedObject var store: AnySecretStore
var secret: AnySecret
@Binding var activeSecret: AnySecret.ID?
@State var isDeleting: Bool = false
@State var isRenaming: Bool = false
var deletedSecret: (AnySecret) -> Void
var renamedSecret: (AnySecret) -> Void
var body: some View {
let showingPopupWrapped = Binding(
get: { isDeleting || isRenaming },
set: { if $0 == false { isDeleting = false; isRenaming = false } }
)
return NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: $activeSecret) {
Text(secret.name)
}.contextMenu {
if store is AnySecretStoreModifiable {
Button(action: { isRenaming = true }) {
Text("Rename")
}
Button(action: { isDeleting = true }) {
Text("Delete")
}
}
}
.popover(isPresented: showingPopupWrapped) {
if let modifiable = store as? AnySecretStoreModifiable {
if isDeleting {
DeleteSecretView(store: modifiable, secret: secret) { deleted in
isDeleting = false
if deleted {
deletedSecret(secret)
}
}
} else if isRenaming {
RenameSecretView(store: modifiable, secret: secret) { renamed in
isRenaming = false
if renamed {
renamedSecret(secret)
}
}
}
}
}
}
}

View File

@@ -1,41 +0,0 @@
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")
}
}
}
.popover(isPresented: .constant(deletingSecret == secret)) {
if let modifiable = store as? AnySecretStoreModifiable {
DeleteSecretView(store: modifiable, secret: secret) { deleted in
deletingSecret = nil
if deleted {
deletedSecret(AnySecret(secret))
}
}
}
}
}
}
func delete<SecretType: Secret>(secret: SecretType) {
deletingSecret = AnySecret(secret)
}
}

View File

@@ -6,10 +6,17 @@ struct StoreListView: View {
@Binding var showingCreation: Bool
@State private var activeSecret: AnySecret.ID?
@State private var deletingSecret: AnySecret?
@EnvironmentObject private var storeList: SecretStoreList
private func secretDeleted(secret: AnySecret) {
activeSecret = nextDefaultSecret
}
private func secretRenamed(secret: AnySecret) {
activeSecret = nextDefaultSecret
}
var body: some View {
NavigationView {
List(selection: $activeSecret) {
@@ -19,9 +26,15 @@ struct StoreListView: View {
if store.secrets.isEmpty {
EmptyStoreView(store: store, activeSecret: $activeSecret)
} else {
SecretListView(store: store, activeSecret: $activeSecret, deletingSecret: $deletingSecret, deletedSecret: { _ in
activeSecret = nextDefaultSecret
})
ForEach(store.secrets) { secret in
SecretListItemView(
store: store,
secret: secret,
activeSecret: $activeSecret,
deletedSecret: self.secretDeleted,
renamedSecret: self.secretRenamed
)
}
}
}
}
@@ -33,9 +46,7 @@ struct StoreListView: View {
}
.frame(minWidth: 100, idealWidth: 240)
}
}
}
extension StoreListView {