From bcf1d25b63b5efce781606910827e6abd5f2a3db Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Mon, 20 Jan 2025 22:37:00 +0100 Subject: [PATCH 01/14] PWD-1: create custom button component --- password/passwordApp.swift | 8 ++- password/ui/components/PwdButton.swift | 99 ++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 password/ui/components/PwdButton.swift diff --git a/password/passwordApp.swift b/password/passwordApp.swift index 6357c27..3c136f3 100644 --- a/password/passwordApp.swift +++ b/password/passwordApp.swift @@ -51,9 +51,11 @@ struct passwordApp: App { if isAuthenticated { ContextWrapper() } else { - Button("Authenticate") { - authenticate() - } + PwdButton( + label: Text("Authenticate"), + variant: .primary, + action: authenticate + ) } } .onAppear { diff --git a/password/ui/components/PwdButton.swift b/password/ui/components/PwdButton.swift new file mode 100644 index 0000000..7c2db48 --- /dev/null +++ b/password/ui/components/PwdButton.swift @@ -0,0 +1,99 @@ +// +// Button.swift +// password +// +// Created by Markus Thielker on 19.01.25. +// + +import SwiftUI + +struct PwdButton: View { + + @Environment(\.colorScheme) private var colorScheme + + var label: Label + var variant: ButtonVariant = .default + var size: ButtonSize = .medium + var action: () -> Void + + var body: some View { + HStack { + label + } + .padding(size.padding) + .background(variant.backgroundColor(colorScheme)) + .foregroundColor(variant.foregroundColor(colorScheme)) + .font(size.font) + .cornerRadius(size.cornerRadius) + .overlay( + RoundedRectangle(cornerRadius: size.cornerRadius) + .stroke(variant.borderColor(colorScheme), lineWidth: variant.borderWidth) + ) + .onTapGesture { + action() + } + } +} + +enum ButtonVariant { + + case `default`, primary, secondary, outline, ghost + + func backgroundColor(_ colorScheme: ColorScheme) -> Color { + switch self { + case .primary: return .blue + case .secondary: return .gray + case .outline, .ghost: return .clear + default: return .blue + } + } + + func foregroundColor(_ colorScheme: ColorScheme) -> Color { + switch self { + case .primary, .secondary, .default: return .white + case .outline, .ghost: return colorScheme == .dark ? .white : .black + } + } + + func borderColor(_ colorScheme: ColorScheme) -> Color { + switch self { + case .outline: return .gray + default: return .clear + } + } + + var borderWidth: CGFloat { + switch self { + case .outline: return 1 + default: return 0 + } + } +} + +enum ButtonSize { + case small, medium, large + + var padding: EdgeInsets { + switch self { + case .small: return EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12) + case .medium: return EdgeInsets(top: 10, leading: 16, bottom: 10, trailing: 16) + case .large: return EdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20) + } + } + + var font: Font { + switch self { + case .small: return .caption + case .medium: return .body + case .large: return .title3 + } + } + + var cornerRadius: CGFloat { + switch self { + case .small: return 6 + case .medium: return 8 + case .large: return 10 + } + } +} -- 2.47.2 From 6156a42a0ef7064639f1dba69ee65cd848772df3 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Mon, 20 Jan 2025 22:50:12 +0100 Subject: [PATCH 02/14] PWD-1: Add button preview --- password/ui/components/PwdButton.swift | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/password/ui/components/PwdButton.swift b/password/ui/components/PwdButton.swift index 7c2db48..9abe8e6 100644 --- a/password/ui/components/PwdButton.swift +++ b/password/ui/components/PwdButton.swift @@ -97,3 +97,33 @@ enum ButtonSize { } } } + +#Preview { + VStack { + + PwdButton( + label: Text("Click me!"), + variant: .primary, + action: {} + ) + + PwdButton( + label: Text("Click me!"), + variant: .secondary, + action: {} + ) + + PwdButton( + label: Text("Click me!"), + variant: .outline, + action: {} + ) + + PwdButton( + label: Text("Click me!"), + variant: .ghost, + action: {} + ) + + }.padding() +} -- 2.47.2 From 594aa204ce5bb6675ed62ae295b77225e51e6e77 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Mon, 20 Jan 2025 22:50:37 +0100 Subject: [PATCH 03/14] PWD-1: Add debug conditional to authentication call --- password/passwordApp.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/password/passwordApp.swift b/password/passwordApp.swift index 3c136f3..3b32951 100644 --- a/password/passwordApp.swift +++ b/password/passwordApp.swift @@ -59,7 +59,9 @@ struct passwordApp: App { } } .onAppear { - authenticate() + #if !DEBUG + authenticate() + #endif } } .modelContainer(for: [Password.self, PasswordAttempt.self]) -- 2.47.2 From b93316c2ac794b408165ecd7d193168c82a068d4 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Mon, 20 Jan 2025 22:55:14 +0100 Subject: [PATCH 04/14] PWD-1: update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 315f335..e5f8427 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ DerivedData # macOS build/ *.app + +# intelliJ +.idea -- 2.47.2 From 0435cb45f6d66f8d6c9120055545955b7acc7425 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 14:19:54 +0100 Subject: [PATCH 05/14] PWD-1: add icon button variant --- password/ui/components/PwdButton.swift | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/password/ui/components/PwdButton.swift b/password/ui/components/PwdButton.swift index 9abe8e6..c08598e 100644 --- a/password/ui/components/PwdButton.swift +++ b/password/ui/components/PwdButton.swift @@ -71,13 +71,14 @@ enum ButtonVariant { } enum ButtonSize { - case small, medium, large + case small, medium, large, icon var padding: EdgeInsets { switch self { - case .small: return EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12) - case .medium: return EdgeInsets(top: 10, leading: 16, bottom: 10, trailing: 16) - case .large: return EdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20) + case .small: return EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12) + case .medium: return EdgeInsets(top: 10, leading: 16, bottom: 10, trailing: 16) + case .large: return EdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20) + case .icon: return EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10) } } @@ -86,6 +87,7 @@ enum ButtonSize { case .small: return .caption case .medium: return .body case .large: return .title3 + case .icon: return .body } } @@ -94,6 +96,7 @@ enum ButtonSize { case .small: return 6 case .medium: return 8 case .large: return 10 + case .icon: return 8 } } } @@ -125,5 +128,12 @@ enum ButtonSize { action: {} ) + PwdButton( + label: Image(systemName: "plus"), + variant: .primary, + size: .icon, + action: {} + ) + }.padding() } -- 2.47.2 From 7b82797cb5fac68537eeaa04f737c827ec39c3a7 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 14:25:03 +0100 Subject: [PATCH 06/14] PWD-1: add and apply input style --- password/ui/styles/PwdTextFieldStyle.swift | 22 ++++++++++++++++++++++ password/view/add/AddView.swift | 2 ++ password/view/detail/DetailView.swift | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 password/ui/styles/PwdTextFieldStyle.swift diff --git a/password/ui/styles/PwdTextFieldStyle.swift b/password/ui/styles/PwdTextFieldStyle.swift new file mode 100644 index 0000000..bb2783d --- /dev/null +++ b/password/ui/styles/PwdTextFieldStyle.swift @@ -0,0 +1,22 @@ +// +// ModernInputStyle.swift +// password +// +// Created by Markus Thielker on 19.01.25. +// + +import SwiftUI + +struct PwdTextFieldStyle: TextFieldStyle { + + @Environment(\.colorScheme) var colorScheme + + func _body(configuration: TextField) -> some View { + configuration + .padding(EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)) + .background(colorScheme == .dark ? .black : .white) + .foregroundColor(colorScheme == .dark ? .white : .black) + .textFieldStyle(.plain) + .cornerRadius(12) + } +} diff --git a/password/view/add/AddView.swift b/password/view/add/AddView.swift index 0fff1ad..4e58d9d 100644 --- a/password/view/add/AddView.swift +++ b/password/view/add/AddView.swift @@ -26,7 +26,9 @@ struct AddPasswordView: View { .padding(EdgeInsets(top: 0, leading: 0, bottom: 10, trailing: 0)) Form { TextField("Name", text: $name) + .textFieldStyle(PwdTextFieldStyle()) TextField("Value", text: $value) + .textFieldStyle(PwdTextFieldStyle()) Text("The password will not be visible again later. Make sure to save it somewhere else too!") .font(.footnote) HStack { diff --git a/password/view/detail/DetailView.swift b/password/view/detail/DetailView.swift index 0c7bb93..54ab9fb 100644 --- a/password/view/detail/DetailView.swift +++ b/password/view/detail/DetailView.swift @@ -28,7 +28,7 @@ struct DetailView: View { Text("Enter the password for \(viewModel.password.name) and submit with \"Enter\"") Form { SecureField("", text: $value) - .textFieldStyle(RoundedBorderTextFieldStyle()) + .textFieldStyle(PwdTextFieldStyle()) .onChange(of: value) { _, _ in if (value.isEmpty){ startTime = nil -- 2.47.2 From 7c1b9cb96da0f7a1635a6d7e10a239a00a42f97b Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 14:28:50 +0100 Subject: [PATCH 07/14] PWD-1: raise macOS deployment target to 15.0 This is done to use the NavigationStack component in the following navigation refactoring to remove the NavigationView --- password.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/password.xcodeproj/project.pbxproj b/password.xcodeproj/project.pbxproj index 56e343b..270ea8f 100644 --- a/password.xcodeproj/project.pbxproj +++ b/password.xcodeproj/project.pbxproj @@ -413,7 +413,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.thielker.password; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -441,7 +441,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.thielker.password; PRODUCT_NAME = "$(TARGET_NAME)"; -- 2.47.2 From a4dce65275d9e1573ffca9f58a7480b8ff15e1c1 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 14:29:15 +0100 Subject: [PATCH 08/14] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 700cbac..b245b5d 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,6 @@ Gamification like success rates, typing time tracking and daily prompts to enter ## Security An app handling secrets like these has to be implemented securly. That's why: -- Passwords are securly stored in your devices keychain +- Passwords are securely stored in your devices keychain - Authentication is required to enter the app - No cloud sync (unless you are using your iCloud Keychain) -- 2.47.2 From 63d2c9e6ebe16da578a0a66c3661a73b2717aaed Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 16:03:00 +0100 Subject: [PATCH 09/14] PWD-1: add border to text field style --- password/ui/styles/PwdTextFieldStyle.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/password/ui/styles/PwdTextFieldStyle.swift b/password/ui/styles/PwdTextFieldStyle.swift index bb2783d..16adef3 100644 --- a/password/ui/styles/PwdTextFieldStyle.swift +++ b/password/ui/styles/PwdTextFieldStyle.swift @@ -11,12 +11,18 @@ struct PwdTextFieldStyle: TextFieldStyle { @Environment(\.colorScheme) var colorScheme + private let radius: CGFloat = 12 + func _body(configuration: TextField) -> some View { configuration .padding(EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)) .background(colorScheme == .dark ? .black : .white) .foregroundColor(colorScheme == .dark ? .white : .black) .textFieldStyle(.plain) - .cornerRadius(12) + .cornerRadius(radius) + .overlay( + RoundedRectangle(cornerRadius: radius) + .stroke(.gray, lineWidth: 1) + ) } } -- 2.47.2 From ca240988b8eb80237978d6a15879db1ef2019687 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 16:03:29 +0100 Subject: [PATCH 10/14] PWD-1: refactor button component --- password/ui/components/PwdButton.swift | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/password/ui/components/PwdButton.swift b/password/ui/components/PwdButton.swift index c08598e..f2ed6ab 100644 --- a/password/ui/components/PwdButton.swift +++ b/password/ui/components/PwdButton.swift @@ -17,21 +17,19 @@ struct PwdButton: View { var action: () -> Void var body: some View { - HStack { + Button(action: action) { label } - .padding(size.padding) - .background(variant.backgroundColor(colorScheme)) - .foregroundColor(variant.foregroundColor(colorScheme)) - .font(size.font) - .cornerRadius(size.cornerRadius) - .overlay( - RoundedRectangle(cornerRadius: size.cornerRadius) - .stroke(variant.borderColor(colorScheme), lineWidth: variant.borderWidth) - ) - .onTapGesture { - action() - } + .buttonStyle(PlainButtonStyle()) + .padding(size.padding) + .background(variant.backgroundColor(colorScheme)) + .foregroundColor(variant.foregroundColor(colorScheme)) + .font(size.font) + .cornerRadius(size.cornerRadius) + .overlay( + RoundedRectangle(cornerRadius: size.cornerRadius) + .stroke(variant.borderColor(colorScheme), lineWidth: variant.borderWidth) + ) } } -- 2.47.2 From 4887f42b1f88025d6a382fae93cc2225482a191b Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 16:04:37 +0100 Subject: [PATCH 11/14] PWD-1: replace NavigationView with NavigationStack implementation --- password/view/list/ListView.swift | 75 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/password/view/list/ListView.swift b/password/view/list/ListView.swift index a6d3ae6..129b79b 100644 --- a/password/view/list/ListView.swift +++ b/password/view/list/ListView.swift @@ -11,35 +11,70 @@ import _SwiftData_SwiftUI struct ListView: View { @Environment(\.modelContext) var context + @Environment(\.colorScheme) var colorScheme @ObservedObject var viewModel: ListViewModel + @State var isAddingPassword: Bool = false + @State var selectedItem: UUID? var body: some View { - NavigationView { - List { - ForEach(viewModel.passwords) { password in - NavigationLink(destination: DetailView(viewModel: DetailViewModel(context: context, passwordID: password.id))) { - Text(password.name) + NavigationStack { + HStack { + List { + HStack { + Text("Your passwords") + .fontWeight(.bold) + + Spacer() + + PwdButton( + label: Image(systemName: "plus"), + variant: .primary, + size: .icon, + action: { isAddingPassword = true } + ) + PwdButton( + label: Image(systemName: "arrow.trianglehead.clockwise") + .imageScale(.small), + variant: .primary, + size: .icon, + action: { + viewModel.passwords = viewModel.getAllPasswords() + } + ) + } + .background(.clear) + .frame(maxWidth: .infinity) + + ForEach(viewModel.passwords) { password in + Button("\(password.name)") { + selectedItem = password.id + } + .frame(maxWidth: .infinity, alignment: .leading) + .buttonStyle(PlainButtonStyle()) + .padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8)) + .background(selectedItem == password.id ? .blue : .clear) + .foregroundColor(selectedItem == password.id ? .white : (colorScheme == .dark ? .white : .black)) + .cornerRadius(8) + } + } + .frame(width: 250) + .listStyle(SidebarListStyle()) + + if let selectedItem = selectedItem { + DetailView(viewModel: DetailViewModel(context: context, passwordID: selectedItem)) + } else { + HStack { + Spacer() + Text("Select a password") + Spacer() } } } - .scrollContentBackground(.hidden) - .toolbar { - Button(action: { isAddingPassword = true }) { - Image(systemName: "plus") - .frame(width: 20, height: 20) - } - Button(action: { - viewModel.passwords = viewModel.getAllPasswords() - }) { - Image(systemName: "arrow.trianglehead.clockwise") - .imageScale(.medium) - .frame(width: 20, height: 20) - } - } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) } - .navigationTitle("Password Trainer") + .windowToolbarFullScreenVisibility(.onHover) .sheet(isPresented: $isAddingPassword) { AddPasswordView(viewModel: viewModel) } -- 2.47.2 From bb4cb2a0d3d72353324a6e5594cc0f28bdf37c22 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Tue, 21 Jan 2025 16:06:14 +0100 Subject: [PATCH 12/14] PWD-1: revert button changes --- password/ui/components/PwdButton.swift | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/password/ui/components/PwdButton.swift b/password/ui/components/PwdButton.swift index f2ed6ab..b4ade36 100644 --- a/password/ui/components/PwdButton.swift +++ b/password/ui/components/PwdButton.swift @@ -17,19 +17,22 @@ struct PwdButton: View { var action: () -> Void var body: some View { - Button(action: action) { + HStack{ label } - .buttonStyle(PlainButtonStyle()) - .padding(size.padding) - .background(variant.backgroundColor(colorScheme)) - .foregroundColor(variant.foregroundColor(colorScheme)) - .font(size.font) - .cornerRadius(size.cornerRadius) - .overlay( - RoundedRectangle(cornerRadius: size.cornerRadius) - .stroke(variant.borderColor(colorScheme), lineWidth: variant.borderWidth) - ) + .buttonStyle(PlainButtonStyle()) + .padding(size.padding) + .background(variant.backgroundColor(colorScheme)) + .foregroundColor(variant.foregroundColor(colorScheme)) + .font(size.font) + .cornerRadius(size.cornerRadius) + .overlay( + RoundedRectangle(cornerRadius: size.cornerRadius) + .stroke(variant.borderColor(colorScheme), lineWidth: variant.borderWidth) + ) + .onTapGesture { + action() + } } } -- 2.47.2 From 6b1be3378a61387632f5a0c57fa85b8e3a992024 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Thu, 23 Jan 2025 17:30:49 +0100 Subject: [PATCH 13/14] PWD-1: display text after updating list --- password/view/list/ListView.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/password/view/list/ListView.swift b/password/view/list/ListView.swift index 129b79b..06e2eda 100644 --- a/password/view/list/ListView.swift +++ b/password/view/list/ListView.swift @@ -15,8 +15,9 @@ struct ListView: View { @ObservedObject var viewModel: ListViewModel - @State var isAddingPassword: Bool = false - @State var selectedItem: UUID? + @State private var isAddingPassword: Bool = false + @State private var isUpdateTextVisible: Bool = false + @State private var selectedItem: UUID? var body: some View { NavigationStack { @@ -41,6 +42,10 @@ struct ListView: View { size: .icon, action: { viewModel.passwords = viewModel.getAllPasswords() + isUpdateTextVisible = true + DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + isUpdateTextVisible = false + } } ) } @@ -58,6 +63,11 @@ struct ListView: View { .foregroundColor(selectedItem == password.id ? .white : (colorScheme == .dark ? .white : .black)) .cornerRadius(8) } + + if isUpdateTextVisible { + Text("List updated") + .animation(.easeInOut(duration: 1), value: isUpdateTextVisible) + } } .frame(width: 250) .listStyle(SidebarListStyle()) -- 2.47.2 From d2c1f1dc22a658085c3165a11583e2318bb0aba9 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Fri, 24 Jan 2025 13:43:33 +0100 Subject: [PATCH 14/14] PWD-1: modify text styling --- password/view/detail/DetailView.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/password/view/detail/DetailView.swift b/password/view/detail/DetailView.swift index 54ab9fb..a894cda 100644 --- a/password/view/detail/DetailView.swift +++ b/password/view/detail/DetailView.swift @@ -25,7 +25,13 @@ struct DetailView: View { var body: some View { VStack { - Text("Enter the password for \(viewModel.password.name) and submit with \"Enter\"") + VStack { + Text(viewModel.password.name) + .font(.title) + .foregroundColor(.primary) + Text("Enter the password and submit with \"Enter\"") + .font(.title3) + } Form { SecureField("", text: $value) .textFieldStyle(PwdTextFieldStyle()) -- 2.47.2