From b79e11fd373d0a0a3ad39f91e5c05bf205c3a650 Mon Sep 17 00:00:00 2001 From: Goncalo-FradeIOHK Date: Thu, 15 Dec 2022 13:40:12 +0000 Subject: [PATCH] feat(sample): Third batch of sample app code --- PrismAgent/Sources/PrismAgent.swift | 4 + .../project.pbxproj | 40 +++++++ .../ProofOfRequestBuilder.swift | 24 ++++ .../ProofOfRequest/ProofOfRequestState.swift | 27 +++++ .../ProofOfRequest/ProofOfRequestView.swift | 72 +++++++++++ .../ProofOfRequestViewModel.swift | 113 ++++++++++++++++++ .../UI/ProofOfRequestCheckView.swift | 88 ++++++++++++++ .../WalletDemo/UIHelper/CheckButton.swift | 22 ++++ 8 files changed, 390 insertions(+) create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift diff --git a/PrismAgent/Sources/PrismAgent.swift b/PrismAgent/Sources/PrismAgent.swift index ab3c1bca..954bcb78 100644 --- a/PrismAgent/Sources/PrismAgent.swift +++ b/PrismAgent/Sources/PrismAgent.swift @@ -341,4 +341,8 @@ public class PrismAgent { .flatMap { $0.publisher } .eraseToAnyPublisher() } + + public func verifiableCredentials() -> AnyPublisher<[VerifiableCredential], Error> { + pluto.getAllCredentials() + } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj index 1a5a5f2b..76ffdf24 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj @@ -79,6 +79,12 @@ EE75A32B294A822C007D4405 /* ErrorDialogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A32A294A822C007D4405 /* ErrorDialogView.swift */; }; EE75A32D294A82A4007D4405 /* RoundedRectWithBorderText.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A32C294A82A4007D4405 /* RoundedRectWithBorderText.swift */; }; EE75A32F294A8D55007D4405 /* DisplayErrorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A32E294A8D55007D4405 /* DisplayErrorState.swift */; }; + EE75A337294A905F007D4405 /* ProofOfRequestCheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A332294A905F007D4405 /* ProofOfRequestCheckView.swift */; }; + EE75A338294A905F007D4405 /* ProofOfRequestBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A333294A905F007D4405 /* ProofOfRequestBuilder.swift */; }; + EE75A339294A905F007D4405 /* ProofOfRequestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A334294A905F007D4405 /* ProofOfRequestState.swift */; }; + EE75A33A294A905F007D4405 /* ProofOfRequestViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A335294A905F007D4405 /* ProofOfRequestViewModel.swift */; }; + EE75A33B294A905F007D4405 /* ProofOfRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A336294A905F007D4405 /* ProofOfRequestView.swift */; }; + EE75A33D294A9172007D4405 /* CheckButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A33C294A9172007D4405 /* CheckButton.swift */; }; EEB7D32229420180006E076D /* SetupPrismAgentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEB7D32129420180006E076D /* SetupPrismAgentView.swift */; }; EEB7D3242942018C006E076D /* SetupPrismAgentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEB7D3232942018C006E076D /* SetupPrismAgentViewModel.swift */; }; EEE61FBA2937CA280053AE52 /* AtalaPrismWalletDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE61FB92937CA280053AE52 /* AtalaPrismWalletDemoApp.swift */; }; @@ -160,6 +166,12 @@ EE75A32A294A822C007D4405 /* ErrorDialogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorDialogView.swift; sourceTree = ""; }; EE75A32C294A82A4007D4405 /* RoundedRectWithBorderText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedRectWithBorderText.swift; sourceTree = ""; }; EE75A32E294A8D55007D4405 /* DisplayErrorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayErrorState.swift; sourceTree = ""; }; + EE75A332294A905F007D4405 /* ProofOfRequestCheckView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestCheckView.swift; sourceTree = ""; }; + EE75A333294A905F007D4405 /* ProofOfRequestBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestBuilder.swift; sourceTree = ""; }; + EE75A334294A905F007D4405 /* ProofOfRequestState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestState.swift; sourceTree = ""; }; + EE75A335294A905F007D4405 /* ProofOfRequestViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestViewModel.swift; sourceTree = ""; }; + EE75A336294A905F007D4405 /* ProofOfRequestView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestView.swift; sourceTree = ""; }; + EE75A33C294A9172007D4405 /* CheckButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckButton.swift; sourceTree = ""; }; EEB7D32129420180006E076D /* SetupPrismAgentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPrismAgentView.swift; sourceTree = ""; }; EEB7D3232942018C006E076D /* SetupPrismAgentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPrismAgentViewModel.swift; sourceTree = ""; }; EEE61FB62937CA280053AE52 /* AtalaPrismWalletDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AtalaPrismWalletDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -228,6 +240,7 @@ EE6C38CA29462474006CD2D3 /* WalletDemo */ = { isa = PBXGroup; children = ( + EE75A330294A905F007D4405 /* ProofOfRequest */, EE75A314294A7CAE007D4405 /* AddNewContact */, EE75A309294A34E1007D4405 /* CredentialDetail */, EE75A2FC294A33D0007D4405 /* CredentialsList */, @@ -264,6 +277,7 @@ EE75A32A294A822C007D4405 /* ErrorDialogView.swift */, EE75A32C294A82A4007D4405 /* RoundedRectWithBorderText.swift */, EE75A32E294A8D55007D4405 /* DisplayErrorState.swift */, + EE75A33C294A9172007D4405 /* CheckButton.swift */, ); path = UIHelper; sourceTree = ""; @@ -377,6 +391,26 @@ path = UI; sourceTree = ""; }; + EE75A330294A905F007D4405 /* ProofOfRequest */ = { + isa = PBXGroup; + children = ( + EE75A331294A905F007D4405 /* UI */, + EE75A333294A905F007D4405 /* ProofOfRequestBuilder.swift */, + EE75A334294A905F007D4405 /* ProofOfRequestState.swift */, + EE75A335294A905F007D4405 /* ProofOfRequestViewModel.swift */, + EE75A336294A905F007D4405 /* ProofOfRequestView.swift */, + ); + path = ProofOfRequest; + sourceTree = ""; + }; + EE75A331294A905F007D4405 /* UI */ = { + isa = PBXGroup; + children = ( + EE75A332294A905F007D4405 /* ProofOfRequestCheckView.swift */, + ); + path = UI; + sourceTree = ""; + }; EEB7D32029420155006E076D /* SetupPrismAgent */ = { isa = PBXGroup; children = ( @@ -590,6 +624,7 @@ files = ( EE75A303294A33D0007D4405 /* CredentialsListViewModel.swift in Sources */, EE75A2FB29487F36007D4405 /* MainViewModel.swift in Sources */, + EE75A33D294A9172007D4405 /* CheckButton.swift in Sources */, EE75A323294A7CAE007D4405 /* AddNewContactViewModel.swift in Sources */, EEB7D3242942018C006E076D /* SetupPrismAgentViewModel.swift in Sources */, EEE620132937F1D40053AE52 /* PrintObjects.swift in Sources */, @@ -599,6 +634,7 @@ EE6C39172946851D006CD2D3 /* QRCodeScannerBuilder.swift in Sources */, EE6C39152946851D006CD2D3 /* QRScannerView.swift in Sources */, EEE61FE02937CEAA0053AE52 /* SeedViewModel.swift in Sources */, + EE75A33A294A905F007D4405 /* ProofOfRequestViewModel.swift in Sources */, EE75A302294A33D0007D4405 /* CredentialsListView.swift in Sources */, EE6C393929468533006CD2D3 /* HomeState.swift in Sources */, EE6C38DC294626E1006CD2D3 /* String+extensions.swift in Sources */, @@ -606,6 +642,7 @@ EEE61FBC2937CA280053AE52 /* FuncionalitiesList.swift in Sources */, EE75A327294A7D40007D4405 /* Button+Configuration.swift in Sources */, EE6C3909294683C4006CD2D3 /* DashboardRouter.swift in Sources */, + EE75A339294A905F007D4405 /* ProofOfRequestState.swift in Sources */, EEE61FDD2937CD7A0053AE52 /* SeedFuncionalitiesView.swift in Sources */, EE75A325294A7D0F007D4405 /* AtalaButton.swift in Sources */, EE6C38EE29467A9D006CD2D3 /* DisablePreferenceKey.swift in Sources */, @@ -616,6 +653,7 @@ EE75A32B294A822C007D4405 /* ErrorDialogView.swift in Sources */, EE75A305294A33D0007D4405 /* CredentialsListRouter.swift in Sources */, EE75A31E294A7CAE007D4405 /* AlreadyConnectedView.swift in Sources */, + EE75A337294A905F007D4405 /* ProofOfRequestCheckView.swift in Sources */, EE75A311294A34E1007D4405 /* CredentialDetailNeoView.swift in Sources */, EE75A30E294A34E1007D4405 /* CredentialDetailViewModel.swift in Sources */, EE6C38EC29467A7A006CD2D3 /* ClearFullCoverModifier.swift in Sources */, @@ -645,6 +683,7 @@ EE6C393129468533006CD2D3 /* ActivityListView.swift in Sources */, EE75A308294A346B007D4405 /* NavigationTypeUtilModifiers.swift in Sources */, EE6C390329468288006CD2D3 /* Builder.swift in Sources */, + EE75A338294A905F007D4405 /* ProofOfRequestBuilder.swift in Sources */, EE6C39182946851D006CD2D3 /* QRCodeScannerView.swift in Sources */, EE6C38EA29467A36006CD2D3 /* DashboardView.swift in Sources */, EE75A320294A7CAE007D4405 /* InsertCodeView.swift in Sources */, @@ -654,6 +693,7 @@ EEB7D32229420180006E076D /* SetupPrismAgentView.swift in Sources */, EE6C38F029468196006CD2D3 /* DIContainer.swift in Sources */, EE75A306294A33D0007D4405 /* CredentialsListState.swift in Sources */, + EE75A33B294A905F007D4405 /* ProofOfRequestView.swift in Sources */, EE6C393829468533006CD2D3 /* HomeBuilder.swift in Sources */, EE75A32F294A8D55007D4405 /* DisplayErrorState.swift in Sources */, EEE620162937F3110053AE52 /* SigningVerificationViewModel.swift in Sources */, diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift new file mode 100644 index 00000000..e3d10455 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift @@ -0,0 +1,24 @@ +import Combine +import Foundation +import PrismAgent +import SwiftUI + +struct ProofOfRequestComponent: ComponentContainer { + let proofOfRequest: RequestPresentation + let container: DIContainer +} + +struct ProofOfRequestBuilder: Builder { + func build(component: ProofOfRequestComponent) -> some View { + let viewModel = getViewModel(component: component) { + ProofOfRequestViewModelImpl( + proofOfRequest: component.proofOfRequest, + agent: component.container.resolve(type: PrismAgent.self)! + ) + } + return ProofOfRequestView(viewModel: viewModel) + .onDisappear { + component.container.unregister(type: ProofOfRequestViewModelImpl.self) + } + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift new file mode 100644 index 00000000..68d03553 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift @@ -0,0 +1,27 @@ +import Foundation +struct ProofOfRequestState { + enum FlowStep { + case loading + case shareCredentials + case confirm + case error(DisplayError) + } + + enum RequestedCredentials { + case idCredential + case universityDegree + case proofOfEmployment + case insurance + case custom(String) + } + + struct Contact { + let text: String +// let credentialsRequested: [RequestedCredentials] + } + + struct Credential { + let id: String + let text: String + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift new file mode 100644 index 00000000..5c1bf5ed --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift @@ -0,0 +1,72 @@ +import SwiftUI + +protocol ProofOfRequestViewModel: ProofOfRequestCheckViewModel { + var flowStep: ProofOfRequestState.FlowStep { get } + var dismiss: Bool { get } + func viewDidAppear() + func confirmDismiss() +} + +struct ProofOfRequestView: View { + @StateObject var viewModel: ViewModel + @Environment(\.presentationMode) var presentationMode + + var body: some View { + VStack { + switch viewModel.flowStep { + case .loading: + ProgressView() + .progressViewStyle( + CircularProgressViewStyle() + ) + .onAppear(perform: { + self.viewModel.viewDidAppear() + }) + .frame(maxWidth: .infinity, maxHeight: 55) + .commitDisablePreference() + .disabled(true) + case .shareCredentials: + ProofOfRequestCheckView() + .environmentObject(viewModel) + .commitDisablePreference() + .disabled(viewModel.loading) + case .confirm: + VStack(spacing: 20) { + Image("img_success") + VStack(spacing: 5) { + Text("credentials_detail_share_success_title".localize()) + .font(.caption) + .fontWeight(.light) + Text("credentials_detail_share_success".localize()) + .fontWeight(.heavy) + .foregroundColor(.black) + } + + Divider() + + AtalaButton { + self.viewModel.confirmDismiss() + } label: { + Text("ok".localize()) + } + } + .padding(24) + .commitDisablePreference() + .disabled(viewModel.loading) + case let .error(error): + ErrorDialogView(error: .constant(error)) { + presentationMode.wrappedValue.dismiss() + } + } + } + .background(Color.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .padding() + .animation(.default) + .onChange(of: viewModel.dismiss, perform: { value in + if value { + self.presentationMode.wrappedValue.dismiss() + } + }) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift new file mode 100644 index 00000000..f194ffba --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift @@ -0,0 +1,113 @@ +import Combine +import Foundation +import PrismAgent + +final class ProofOfRequestViewModelImpl: ProofOfRequestViewModel { + @Published var contact: ProofOfRequestState.Contact = .init(text: "") + @Published var flowStep: ProofOfRequestState.FlowStep = .loading + @Published var credential = [ProofOfRequestState.Credential]() + @Published var checks = [Bool]() + @Published var loading = false + @Published var dismiss = false + + private let proofOfRequest: RequestPresentation + private let agent: PrismAgent + private var cancellables = Set() + + init( + proofOfRequest: RequestPresentation, + agent: PrismAgent + ) { + self.proofOfRequest = proofOfRequest + self.agent = agent + } + + func viewDidAppear() { + bind() + } + + func sendPresentation() { + guard !loading else { return } + loading = true + } + + func share() { + + } + + func confirmDismiss() { +// proofOfRequestRepository +// .processedProofOfRequest(proofOfRequest) +// .receive(on: DispatchQueue.main) +// .sink { [weak self] _ in +// self?.dismiss = true +// } receiveValue: {} +// .store(in: &cancellables) + } + + private func bind() { +// let proofOfRequest = proofOfRequest +// +// contactsRepository +// .getAll() +// .map { $0.first { $0.token == proofOfRequest.connectionToken } } +// .first() +// .replaceError(with: nil) +// .dropNil() +// .map { [weak self] in +// self?.contactDomain = $0 +// return ProofOfRequestState.Contact( +// icon: $0.logo.map { .data($0) } ?? .credential, +// text: $0.name, +// credentialsRequested: mapTypeIDs(proofOfRequest.typeIds) +// ) +// } +// .receive(on: DispatchQueue.main) +// .assign(to: &$contact) +// +// credentialsRepository +// .getAll() +// .replaceError(with: []) +// .map { $0.filter { proofOfRequest.typeIds.contains($0.type) } } +// .first() +// .map { +// $0.map { +// ProofOfRequestState.Credential( +// id: $0.id, +// text: $0.credentialName +// ) +// } +// } +// .receive(on: DispatchQueue.main) +// .sink { [weak self] credentials in +// self?.credential = credentials +// self?.checks = credentials.map { _ in false } +// self?.loading = false +// self?.flowStep = .shareCredentials +// } +// .store(in: &cancellables) + } +} + +private func mapTypeIDs( + _ ids: [String] +) -> [ProofOfRequestState.RequestedCredentials] { + ids.map { mapTypeID($0) } +} + +private func mapTypeID( + _ id: String +) -> ProofOfRequestState.RequestedCredentials { + switch id { + case "ID Government": + return .idCredential + case "University Degree": + return .universityDegree + case "Proof of employment": + return .proofOfEmployment + case "Insurance Credential": + return .insurance + default: + return .custom(id) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift new file mode 100644 index 00000000..e4dbc5be --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift @@ -0,0 +1,88 @@ +import SwiftUI + +protocol ProofOfRequestCheckViewModel: ObservableObject { + var contact: ProofOfRequestState.Contact { get } + var credential: [ProofOfRequestState.Credential] { get } + var checks: [Bool] { get set } + var loading: Bool { get } + + func share() +} + +struct ProofOfRequestCheckView: View { + @EnvironmentObject var viewModel: ViewModel + @Environment(\.presentationMode) var presentationMode + + var body: some View { + VStack(alignment: .leading, spacing: 20) { + HStack(spacing: 21) { + Text(viewModel.contact.text) + .fontWeight(.heavy) + .foregroundColor(.black) + Spacer() + } + VStack(alignment: .leading, spacing: 16) { + Text("proof_request_description_first".localize()) + .foregroundColor(.black) + VStack(alignment: .leading, spacing: 8) { + Text("") +// ForEach(Array(viewModel.contact.credentialsRequested.enumerated()), id: \.offset) { +// switch $0.element { +// case .idCredential: +// Text("proof_request_id_credential".localize()) +// .bold() +// case .universityDegree: +// Text("proof_request_university_credential".localize()) +// .bold() +// case .proofOfEmployment: +// Text("proof_request_employment_credential".localize()) +// .bold() +// case .insurance: +// Text("proof_request_insurance_credential".localize()) +// .bold() +// case let .custom(text): +// Text(text) +// .bold() +// } +// } + } + } + + Divider() + + VStack(alignment: .leading, spacing: 8) { + Text("proof_request_select_credentials".localize()) + .font(.caption) + .fontWeight(.light) + .foregroundColor(.black) + VStack(spacing: 12) { + ForEach(viewModel.credential.zipped(), id: \.0) { idx, credential in + HStack { + Text(credential.text) + .bold() + Spacer() + CheckButton(isSelected: $viewModel.checks[idx]) + } + } + } + } + + Divider() + + HStack { + AtalaButton(configuration: .secondary) { + self.presentationMode.wrappedValue.dismiss() + } label: { + Text("cancel".localize()) + } + + AtalaButton(loading: viewModel.loading) { + self.viewModel.share() + } label: { + Text("proof_request_share".localize()) + } + } + } + .padding(24) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift new file mode 100644 index 00000000..ee9e2091 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift @@ -0,0 +1,22 @@ +import SwiftUI + +struct CheckButton: View { + @Binding var isSelected: Bool + + var body: some View { + Button(action: { + withAnimation { + self.isSelected = !isSelected + } + }, label: { + if isSelected { + Image("ico_check_on") + .resizable() + } else { + Image("ico_check_off") + .resizable() + } + }) + .frame(width: 40, height: 40) + } +}