From f5379a5bd0f1911c566b7513b79238d71f29ab31 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:22:43 +0000 Subject: [PATCH 01/12] Add new LoadingView --- LDKNodeMonday.xcodeproj/project.pbxproj | 4 ++++ LDKNodeMonday/View/Home/LoadingView.swift | 26 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 LDKNodeMonday/View/Home/LoadingView.swift diff --git a/LDKNodeMonday.xcodeproj/project.pbxproj b/LDKNodeMonday.xcodeproj/project.pbxproj index 0370ecd4..59940d98 100644 --- a/LDKNodeMonday.xcodeproj/project.pbxproj +++ b/LDKNodeMonday.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 655574802D398A750064F859 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6555747F2D398A750064F859 /* LoadingView.swift */; }; 65875A932CCB9809000D3E70 /* LDKNode in Frameworks */ = {isa = PBXBuildFile; productRef = 65875A922CCB9809000D3E70 /* LDKNode */; }; 659EE6952CF8D4990064ED78 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */; }; 65A406632D070CBD00EA331E /* NetworkSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A406622D070CBD00EA331E /* NetworkSettingsView.swift */; }; @@ -93,6 +94,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 6555747F2D398A750064F859 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = ""; }; 65A406622D070CBD00EA331E /* NetworkSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsView.swift; sourceTree = ""; }; AE00550D2B479EF000100797 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; @@ -305,6 +307,7 @@ AE186B8D2A1540B700338463 /* StartView.swift */, AE4C15B12A1316D100AA8917 /* BitcoinView.swift */, 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */, + 6555747F2D398A750064F859 /* LoadingView.swift */, AE551D402B8ECC2D0034B61E /* Payments */, AE551D412B8ECC390034B61E /* Send */, AE551D422B8ECC400034B61E /* Receive */, @@ -682,6 +685,7 @@ AE7D3FAB2A4263AE00EAE730 /* PaymentsView.swift in Sources */, AE80116D29A59AF4009B9967 /* ChannelAddView.swift in Sources */, AE551D472B8ECE7D0034B61E /* Payment.swift in Sources */, + 655574802D398A750064F859 /* LoadingView.swift in Sources */, AE51BEFA2B37A2A500BAE452 /* Event+Extensions.swift in Sources */, AE80116B29A59976009B9967 /* SettingsView.swift in Sources */, AE49E8522A253618002623E8 /* StartViewModel.swift in Sources */, diff --git a/LDKNodeMonday/View/Home/LoadingView.swift b/LDKNodeMonday/View/Home/LoadingView.swift new file mode 100644 index 00000000..653bc881 --- /dev/null +++ b/LDKNodeMonday/View/Home/LoadingView.swift @@ -0,0 +1,26 @@ +// +// LoadingView.swift +// LDKNodeMonday +// +// Created by Daniel Nordh on 16/01/2025. +// + +import SwiftUI + +struct LoadingView: View { + + var body: some View { + + VStack { + Spacer() + ProgressView() + .progressViewStyle(CircularProgressViewStyle()) + Spacer() + } + } + +} + +#Preview { + LoadingView() +} From 76d826915c7463228c041e75601998dab440fb0f Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:24:03 +0000 Subject: [PATCH 02/12] Use AppState for start/onboarding --- LDKNodeMonday/App/LDKNodeMondayApp.swift | 63 +++++++++++++++++-- .../View Model/Home/OnboardingViewModel.swift | 7 ++- LDKNodeMonday/View/Home/OnboardingView.swift | 5 +- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/LDKNodeMonday/App/LDKNodeMondayApp.swift b/LDKNodeMonday/App/LDKNodeMondayApp.swift index cea0d68b..e29d1c55 100644 --- a/LDKNodeMonday/App/LDKNodeMondayApp.swift +++ b/LDKNodeMonday/App/LDKNodeMondayApp.swift @@ -9,22 +9,66 @@ import SwiftUI @main struct LDKNodeMondayApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate - @AppStorage("isOnboarding") var isOnboarding: Bool = true + @State private var appState = AppState.loading + @State private var appError: Error? @State private var navigationPath = NavigationPath() var body: some Scene { WindowGroup { NavigationStack(path: $navigationPath) { - if isOnboarding { - OnboardingView(viewModel: .init()) - } else { - StartView(viewModel: .init(), navigationPath: $navigationPath) + switch appState { + case .onboarding: + OnboardingView(viewModel: .init(appState: $appState)) + case .wallet: + BitcoinView( + viewModel: .init(priceClient: .live), + sendNavigationPath: $navigationPath + ) + case .error: + LoadingView() // TODO: Replace with error view + default: + LoadingView() } } - .onChange(of: isOnboarding) { oldValue, newValue in + .onChange(of: appState) { oldValue, newValue in navigationPath = NavigationPath() } + .task { + await start() + } + } + } + + func start() async { + var backupInfo: BackupInfo? + + do { + backupInfo = try KeyClient.live.getBackupInfo() + } catch let error { + debugPrint(error) // TODO: Show error on relevant screen, unless this is thrown if no seed has been saved + await MainActor.run { + self.appError = error + self.appState = .error + } + } + + if backupInfo != nil { + do { + try await LightningNodeService.shared.start() // TODO: Start could take parameters from backupInfo (seed, network, url, lsp) + LightningNodeService.shared.listenForEvents() + await MainActor.run { + self.appState = .wallet + } + } catch let error { + debugPrint(error) + self.appError = error + } + } else { + await MainActor.run { + self.appState = .onboarding + } } } } @@ -34,3 +78,10 @@ class AppDelegate: NSObject, UIApplicationDelegate { try? LightningNodeService.shared.stop() } } + +public enum AppState { + case onboarding + case wallet + case loading + case error +} diff --git a/LDKNodeMonday/View Model/Home/OnboardingViewModel.swift b/LDKNodeMonday/View Model/Home/OnboardingViewModel.swift index 19f8b3b7..96b11356 100644 --- a/LDKNodeMonday/View Model/Home/OnboardingViewModel.swift +++ b/LDKNodeMonday/View Model/Home/OnboardingViewModel.swift @@ -9,7 +9,7 @@ import LDKNode import SwiftUI class OnboardingViewModel: ObservableObject { - @AppStorage("isOnboarding") var isOnboarding: Bool? + @Binding var appState: AppState @Published var networkColor = Color.gray @Published var onboardingViewError: MondayError? @Published var seedPhrase: String = "" { @@ -77,7 +77,8 @@ class OnboardingViewModel: ObservableObject { } } - init() { + init(appState: Binding) { + _appState = appState do { if let networkString = try KeyClient.live.getNetwork() { @@ -108,7 +109,7 @@ class OnboardingViewModel: ObservableObject { try KeyClient.live.saveEsploraURL(selectedEsploraServer.url) LightningNodeService.shared = LightningNodeService() DispatchQueue.main.async { - self.isOnboarding = false + self.appState = .wallet } } catch let error as NodeError { let errorString = handleNodeError(error) diff --git a/LDKNodeMonday/View/Home/OnboardingView.swift b/LDKNodeMonday/View/Home/OnboardingView.swift index f2653598..5120329f 100644 --- a/LDKNodeMonday/View/Home/OnboardingView.swift +++ b/LDKNodeMonday/View/Home/OnboardingView.swift @@ -10,8 +10,6 @@ import LDKNode import SwiftUI struct OnboardingView: View { - @AppStorage("isOnboarding") var isOnboarding: Bool? - @AppStorage("isFirstTime") var isFirstTime: Bool = true @ObservedObject var viewModel: OnboardingViewModel @@ -70,7 +68,6 @@ struct OnboardingView: View { Button("Create wallet") { viewModel.saveSeed() - isOnboarding = false } .buttonStyle( BitcoinFilled( @@ -105,6 +102,6 @@ struct OnboardingView: View { #if DEBUG #Preview { - OnboardingView(viewModel: .init()) + OnboardingView(viewModel: .init(appState: .constant(.onboarding))) } #endif From 0305807d76c5dbf5e6beaa376c31858d0547779e Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:25:21 +0000 Subject: [PATCH 03/12] Remove unused StartView and viewModel --- LDKNodeMonday.xcodeproj/project.pbxproj | 8 -- .../View Model/Home/StartViewModel.swift | 54 ---------- LDKNodeMonday/View/Home/StartView.swift | 99 ------------------- 3 files changed, 161 deletions(-) delete mode 100644 LDKNodeMonday/View Model/Home/StartViewModel.swift delete mode 100644 LDKNodeMonday/View/Home/StartView.swift diff --git a/LDKNodeMonday.xcodeproj/project.pbxproj b/LDKNodeMonday.xcodeproj/project.pbxproj index 59940d98..1ae354b8 100644 --- a/LDKNodeMonday.xcodeproj/project.pbxproj +++ b/LDKNodeMonday.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ AE17E8DE29A402E40058C9C9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AE17E8DD29A402E40058C9C9 /* Assets.xcassets */; }; AE17E8E129A402E40058C9C9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AE17E8E029A402E40058C9C9 /* Preview Assets.xcassets */; }; AE17E90D29A42D430058C9C9 /* LightningNodeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE17E90C29A42D430058C9C9 /* LightningNodeService.swift */; }; - AE186B8E2A1540B700338463 /* StartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE186B8D2A1540B700338463 /* StartView.swift */; }; AE1D9BEC2B2A1FFD00620748 /* BitcoinUI in Frameworks */ = {isa = PBXBuildFile; productRef = AE1D9BEB2B2A1FFD00620748 /* BitcoinUI */; }; AE1D9C0B2B2A251500620748 /* ChannelDetails+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1D9C0A2B2A251500620748 /* ChannelDetails+Extensions.swift */; }; AE33D3AE2C7BD0EE00AF562B /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE33D3AD2C7BD0EE00AF562B /* URL+Extensions.swift */; }; @@ -41,7 +40,6 @@ AE38153B2B6A979E006B2952 /* LightningNodeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38153A2B6A979E006B2952 /* LightningNodeInfo.swift */; }; AE4536152C83508E0047D1B2 /* AddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4536142C83508E0047D1B2 /* AddressView.swift */; }; AE49E84C2A24F96F002623E8 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E84B2A24F96F002623E8 /* Constants.swift */; }; - AE49E8522A253618002623E8 /* StartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E8512A253618002623E8 /* StartViewModel.swift */; }; AE49E8542A253647002623E8 /* BitcoinViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E8532A253647002623E8 /* BitcoinViewModel.swift */; }; AE49E8582A2536B4002623E8 /* ChannelsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E8572A2536B4002623E8 /* ChannelsListViewModel.swift */; }; AE49E85A2A2536D4002623E8 /* ChannelAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E8592A2536D4002623E8 /* ChannelAddViewModel.swift */; }; @@ -117,7 +115,6 @@ AE17E8DD29A402E40058C9C9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; AE17E8E029A402E40058C9C9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; AE17E90C29A42D430058C9C9 /* LightningNodeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodeService.swift; sourceTree = ""; }; - AE186B8D2A1540B700338463 /* StartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartView.swift; sourceTree = ""; }; AE1D9C0A2B2A251500620748 /* ChannelDetails+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelDetails+Extensions.swift"; sourceTree = ""; }; AE33D3AD2C7BD0EE00AF562B /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; AE3815352B6A9705006B2952 /* LightningNodesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodesService.swift; sourceTree = ""; }; @@ -125,7 +122,6 @@ AE38153A2B6A979E006B2952 /* LightningNodeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodeInfo.swift; sourceTree = ""; }; AE4536142C83508E0047D1B2 /* AddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressView.swift; sourceTree = ""; }; AE49E84B2A24F96F002623E8 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - AE49E8512A253618002623E8 /* StartViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartViewModel.swift; sourceTree = ""; }; AE49E8532A253647002623E8 /* BitcoinViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinViewModel.swift; sourceTree = ""; }; AE49E8572A2536B4002623E8 /* ChannelsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsListViewModel.swift; sourceTree = ""; }; AE49E8592A2536D4002623E8 /* ChannelAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelAddViewModel.swift; sourceTree = ""; }; @@ -304,7 +300,6 @@ children = ( 65A406622D070CBD00EA331E /* NetworkSettingsView.swift */, AE00550D2B479EF000100797 /* OnboardingView.swift */, - AE186B8D2A1540B700338463 /* StartView.swift */, AE4C15B12A1316D100AA8917 /* BitcoinView.swift */, 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */, 6555747F2D398A750064F859 /* LoadingView.swift */, @@ -319,7 +314,6 @@ isa = PBXGroup; children = ( AE00550F2B479F1100100797 /* OnboardingViewModel.swift */, - AE49E8512A253618002623E8 /* StartViewModel.swift */, AE49E8532A253647002623E8 /* BitcoinViewModel.swift */, AE551D452B8ECE180034B61E /* Payments */, AE43F27A2C7E529C00406326 /* Send */, @@ -622,7 +616,6 @@ buildActionMask = 2147483647; files = ( AE7096352B5C205F0038BE56 /* PriceServiceError.swift in Sources */, - AE186B8E2A1540B700338463 /* StartView.swift in Sources */, AEDF47F32B3FBAB900145D64 /* Notification+Extensions.swift in Sources */, AE0055162B4A0E0100100797 /* Logger+Extensions.swift in Sources */, AE70963C2B5C22270038BE56 /* CurrencyCode.swift in Sources */, @@ -688,7 +681,6 @@ 655574802D398A750064F859 /* LoadingView.swift in Sources */, AE51BEFA2B37A2A500BAE452 /* Event+Extensions.swift in Sources */, AE80116B29A59976009B9967 /* SettingsView.swift in Sources */, - AE49E8522A253618002623E8 /* StartViewModel.swift in Sources */, AE4C15B22A1316D100AA8917 /* BitcoinView.swift in Sources */, AEE5B76A2A09C722001E5E59 /* Peer.swift in Sources */, AE49E84C2A24F96F002623E8 /* Constants.swift in Sources */, diff --git a/LDKNodeMonday/View Model/Home/StartViewModel.swift b/LDKNodeMonday/View Model/Home/StartViewModel.swift deleted file mode 100644 index 3114d68f..00000000 --- a/LDKNodeMonday/View Model/Home/StartViewModel.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// StartViewModel.swift -// LDKNodeMonday -// -// Created by Matthew Ramsden on 5/29/23. -// - -import LDKNode -import SwiftUI - -class StartViewModel: ObservableObject { - @AppStorage("isOnboarding") var isOnboarding: Bool? - @Published var networkColor = Color.gray - @Published var isStarted: Bool = false - @Published var startViewError: MondayError? - - func start() async throws { - do { - try await LightningNodeService.shared.start() - LightningNodeService.shared.listenForEvents() - await MainActor.run { - self.isStarted = true - } - } catch { - await MainActor.run { - self.startViewError = .init( - title: "Node Start Error", - detail: error.localizedDescription - ) - } - throw error - } - } - - func onboarding() { - do { - try KeyClient.live.deleteNetwork() - try KeyClient.live.deleteEsplora() - self.isOnboarding = true - } catch _ as NodeError { - self.isOnboarding = true - } catch { - self.isOnboarding = true - } - } - - func getColor() { - let color = LightningNodeService.shared.networkColor - DispatchQueue.main.async { - self.networkColor = color - } - } - -} diff --git a/LDKNodeMonday/View/Home/StartView.swift b/LDKNodeMonday/View/Home/StartView.swift deleted file mode 100644 index e2b53544..00000000 --- a/LDKNodeMonday/View/Home/StartView.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// StartView.swift -// LDKNodeMonday -// -// Created by Matthew Ramsden on 5/17/23. -// - -import BitcoinUI -import LDKNode -import SwiftUI - -struct StartView: View { - @ObservedObject var viewModel: StartViewModel - @State private var showingStartViewErrorAlert = false - @State var startViewError: MondayError? - @Binding var navigationPath: NavigationPath - - var body: some View { - - ZStack { - Color(uiColor: UIColor.systemBackground) - .ignoresSafeArea() - - if viewModel.isStarted { - BitcoinView( - viewModel: .init(priceClient: .live), - sendNavigationPath: $navigationPath - ) - .edgesIgnoringSafeArea(.all) - } else { - VStack(spacing: 20) { - Image(systemName: "bolt.horizontal") - .symbolEffect(.pulse.wholeSymbol) - .foregroundColor( - Color(red: 119 / 255, green: 243 / 255, blue: 205 / 255) - ) - .padding() - Button { - viewModel.onboarding() - } label: { - HStack { - Image(systemName: "arrowshape.backward") - .minimumScaleFactor(0.5) - Text("Onboarding") - .lineLimit(1) - .minimumScaleFactor(0.5) - } - .foregroundColor(Color(uiColor: UIColor.systemBackground)) - .frame(width: 200, height: 25) - } - .buttonBorderShape(.capsule) - .buttonStyle(.bordered) - .padding() - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(uiColor: UIColor.systemBackground)) - } - } - .onAppear { - Task { - do { - try await viewModel.start() - viewModel.getColor() - } catch let error as NodeError { - let errorString = handleNodeError(error) - DispatchQueue.main.async { - self.startViewError = .init( - title: errorString.title, - detail: errorString.detail - ) - } - } catch { - DispatchQueue.main.async { - self.startViewError = .init( - title: "Unexpected error", - detail: error.localizedDescription - ) - } - } - } - } - .alert(isPresented: $showingStartViewErrorAlert) { - Alert( - title: Text(viewModel.startViewError?.title ?? "Unknown"), - message: Text(viewModel.startViewError?.detail ?? ""), - dismissButton: .default(Text("OK")) { - viewModel.startViewError = nil - } - ) - } - } - -} - -#if DEBUG - #Preview { - StartView(viewModel: .init(), navigationPath: .constant(.init())) - } -#endif From 45d858249f5e9fc66241a77d91abf986480d6e08 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:37:05 +0000 Subject: [PATCH 04/12] Add ErrorView for showing startup errors --- LDKNodeMonday.xcodeproj/project.pbxproj | 4 ++++ LDKNodeMonday/App/LDKNodeMondayApp.swift | 11 ++++++--- LDKNodeMonday/View/Home/ErrorView.swift | 29 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 LDKNodeMonday/View/Home/ErrorView.swift diff --git a/LDKNodeMonday.xcodeproj/project.pbxproj b/LDKNodeMonday.xcodeproj/project.pbxproj index 1ae354b8..7851e7ae 100644 --- a/LDKNodeMonday.xcodeproj/project.pbxproj +++ b/LDKNodeMonday.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 655574802D398A750064F859 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6555747F2D398A750064F859 /* LoadingView.swift */; }; + 655574822D3994E10064F859 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655574812D3994E10064F859 /* ErrorView.swift */; }; 65875A932CCB9809000D3E70 /* LDKNode in Frameworks */ = {isa = PBXBuildFile; productRef = 65875A922CCB9809000D3E70 /* LDKNode */; }; 659EE6952CF8D4990064ED78 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */; }; 65A406632D070CBD00EA331E /* NetworkSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A406622D070CBD00EA331E /* NetworkSettingsView.swift */; }; @@ -93,6 +94,7 @@ /* Begin PBXFileReference section */ 6555747F2D398A750064F859 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; + 655574812D3994E10064F859 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = ""; }; 65A406622D070CBD00EA331E /* NetworkSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsView.swift; sourceTree = ""; }; AE00550D2B479EF000100797 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; @@ -303,6 +305,7 @@ AE4C15B12A1316D100AA8917 /* BitcoinView.swift */, 659EE6942CF8D4990064ED78 /* ImportWalletView.swift */, 6555747F2D398A750064F859 /* LoadingView.swift */, + 655574812D3994E10064F859 /* ErrorView.swift */, AE551D402B8ECC2D0034B61E /* Payments */, AE551D412B8ECC390034B61E /* Send */, AE551D422B8ECC400034B61E /* Receive */, @@ -622,6 +625,7 @@ AE80C2002C4AB360006E7193 /* BIP21View.swift in Sources */, AE01C5B22AB3BF3C00F28C7E /* KeyService.swift in Sources */, AE49E8642A2537B3002623E8 /* PeersListViewModel.swift in Sources */, + 655574822D3994E10064F859 /* ErrorView.swift in Sources */, AE49E8542A253647002623E8 /* BitcoinViewModel.swift in Sources */, AE17E90D29A42D430058C9C9 /* LightningNodeService.swift in Sources */, AEBAA4922A01C34A0042EA82 /* ChannelDetailView.swift in Sources */, diff --git a/LDKNodeMonday/App/LDKNodeMondayApp.swift b/LDKNodeMonday/App/LDKNodeMondayApp.swift index e29d1c55..e7e86a16 100644 --- a/LDKNodeMonday/App/LDKNodeMondayApp.swift +++ b/LDKNodeMonday/App/LDKNodeMondayApp.swift @@ -11,6 +11,7 @@ import SwiftUI struct LDKNodeMondayApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + @State private var appState = AppState.loading @State private var appError: Error? @State private var navigationPath = NavigationPath() @@ -27,7 +28,7 @@ struct LDKNodeMondayApp: App { sendNavigationPath: $navigationPath ) case .error: - LoadingView() // TODO: Replace with error view + ErrorView(error: self.appError) default: LoadingView() } @@ -56,14 +57,18 @@ struct LDKNodeMondayApp: App { if backupInfo != nil { do { - try await LightningNodeService.shared.start() // TODO: Start could take parameters from backupInfo (seed, network, url, lsp) + // TODO: .start could take parameters from backupInfo (seed, network, url, lsp) + try await LightningNodeService.shared.start() LightningNodeService.shared.listenForEvents() await MainActor.run { self.appState = .wallet } } catch let error { debugPrint(error) - self.appError = error + await MainActor.run { + self.appError = error + self.appState = .error + } } } else { await MainActor.run { diff --git a/LDKNodeMonday/View/Home/ErrorView.swift b/LDKNodeMonday/View/Home/ErrorView.swift new file mode 100644 index 00000000..809e0520 --- /dev/null +++ b/LDKNodeMonday/View/Home/ErrorView.swift @@ -0,0 +1,29 @@ +// +// ErrorView.swift +// LDKNodeMonday +// +// Created by Daniel Nordh on 16/01/2025. +// + +import SwiftUI + +struct ErrorView: View { + + var error: Error? + + var body: some View { + Spacer() + Text(error != nil ? error!.localizedDescription : "Unknown error") + Spacer() + } +} + +#Preview { + ErrorView( + error: NSError( + domain: "com.example", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "Unknown error"] + ) + ) +} From 9bee91140bb6a07a087067bfa5b2801f43375705 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:52:14 +0000 Subject: [PATCH 05/12] Coderabbit suggestion Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- LDKNodeMonday/View/Home/ErrorView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LDKNodeMonday/View/Home/ErrorView.swift b/LDKNodeMonday/View/Home/ErrorView.swift index 809e0520..0a8ffc2e 100644 --- a/LDKNodeMonday/View/Home/ErrorView.swift +++ b/LDKNodeMonday/View/Home/ErrorView.swift @@ -13,7 +13,7 @@ struct ErrorView: View { var body: some View { Spacer() - Text(error != nil ? error!.localizedDescription : "Unknown error") + Text(error?.localizedDescription ?? "Unknown error") Spacer() } } From 3e00c7f02576f68b18bf11bca44bb44ff3dd22e3 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:50:39 +0000 Subject: [PATCH 06/12] Remove redundant 'Full Reset' button --- .../View/Settings/SettingsView.swift | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/LDKNodeMonday/View/Settings/SettingsView.swift b/LDKNodeMonday/View/Settings/SettingsView.swift index f620c82f..63e6637d 100644 --- a/LDKNodeMonday/View/Settings/SettingsView.swift +++ b/LDKNodeMonday/View/Settings/SettingsView.swift @@ -125,27 +125,7 @@ struct SettingsView: View { Button("No", role: .cancel) {} } message: { Text( - "All funds will be lost. Are you sure you want to delete the wallet?" - ) - } - - Button { - showResetAppConfirmation = true - } label: { - Text("Full Reset") - }.foregroundColor(.red) - .alert( - "Warning!", - isPresented: $showResetAppConfirmation - ) { - Button("Yes", role: .destructive) { - viewModel.onboarding() - dismiss() - } - Button("No", role: .cancel) {} - } message: { - Text( - "The wallet and all data will be lost. Are you sure you want to fully reset the app?" + "All funds will be lost.\nAre you sure you want to delete the wallet?" ) } @@ -192,6 +172,6 @@ struct SettingsView: View { #if DEBUG #Preview { - SettingsView(viewModel: .init()) + SettingsView(viewModel: .init(appState: .constant(.onboarding))) } #endif From ceffd7a047adf747f9a249beb909b4fac2129c30 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:51:08 +0000 Subject: [PATCH 07/12] Refactor error text --- LDKNodeMonday/View/Home/ErrorView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LDKNodeMonday/View/Home/ErrorView.swift b/LDKNodeMonday/View/Home/ErrorView.swift index 809e0520..0a8ffc2e 100644 --- a/LDKNodeMonday/View/Home/ErrorView.swift +++ b/LDKNodeMonday/View/Home/ErrorView.swift @@ -13,7 +13,7 @@ struct ErrorView: View { var body: some View { Spacer() - Text(error != nil ? error!.localizedDescription : "Unknown error") + Text(error?.localizedDescription ?? "Unknown error") Spacer() } } From 6df4e3a185c0be4d4ce97945cb77343a80d2c0f4 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:51:32 +0000 Subject: [PATCH 08/12] Pass Appstate to bitcoinview --- LDKNodeMonday/App/LDKNodeMondayApp.swift | 2 +- LDKNodeMonday/View Model/Home/BitcoinViewModel.swift | 4 +++- LDKNodeMonday/View/Home/BitcoinView.swift | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/LDKNodeMonday/App/LDKNodeMondayApp.swift b/LDKNodeMonday/App/LDKNodeMondayApp.swift index e7e86a16..be6e1d6e 100644 --- a/LDKNodeMonday/App/LDKNodeMondayApp.swift +++ b/LDKNodeMonday/App/LDKNodeMondayApp.swift @@ -24,7 +24,7 @@ struct LDKNodeMondayApp: App { OnboardingView(viewModel: .init(appState: $appState)) case .wallet: BitcoinView( - viewModel: .init(priceClient: .live), + viewModel: .init(appState: $appState, priceClient: .live), sendNavigationPath: $navigationPath ) case .error: diff --git a/LDKNodeMonday/View Model/Home/BitcoinViewModel.swift b/LDKNodeMonday/View Model/Home/BitcoinViewModel.swift index ac77ac46..c0802529 100644 --- a/LDKNodeMonday/View Model/Home/BitcoinViewModel.swift +++ b/LDKNodeMonday/View Model/Home/BitcoinViewModel.swift @@ -9,6 +9,7 @@ import LDKNode import SwiftUI class BitcoinViewModel: ObservableObject { + @Binding var appState: AppState @Published var bitcoinViewError: MondayError? @Published var networkColor = Color.gray @Published var status: NodeStatus? @@ -33,7 +34,8 @@ class BitcoinViewModel: ObservableObject { return totalUSD } - init(priceClient: PriceClient) { + init(appState: Binding, priceClient: PriceClient) { + _appState = appState self.priceClient = priceClient } diff --git a/LDKNodeMonday/View/Home/BitcoinView.swift b/LDKNodeMonday/View/Home/BitcoinView.swift index b7fad91d..54c806ca 100644 --- a/LDKNodeMonday/View/Home/BitcoinView.swift +++ b/LDKNodeMonday/View/Home/BitcoinView.swift @@ -239,7 +239,7 @@ struct BitcoinView: View { } } ) { - SettingsView(viewModel: .init()) + SettingsView(viewModel: .init(appState: viewModel.$appState)) } .alert(isPresented: $showingBitcoinViewErrorAlert) { Alert( @@ -346,6 +346,9 @@ enum NavigationDestination: Hashable { #if DEBUG #Preview { - BitcoinView(viewModel: .init(priceClient: .mock), sendNavigationPath: .constant(.init())) + BitcoinView( + viewModel: .init(appState: .constant(.onboarding), priceClient: .mock), + sendNavigationPath: .constant(.init()) + ) } #endif From 788a2098b19377e1599dc6edea8db4916bf6b04d Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:52:51 +0000 Subject: [PATCH 09/12] Use AppState in Settingsviewmodel, remove onboarding() --- .../Profile/SettingsViewModel.swift | 67 ++++++++----------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift b/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift index 6f7e6936..7c0b5672 100644 --- a/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift +++ b/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift @@ -10,7 +10,7 @@ import LDKNode import SwiftUI class SettingsViewModel: ObservableObject { - @AppStorage("isOnboarding") var isOnboarding: Bool? + @Binding var appState: AppState @Published var nodeIDError: MondayError? @Published var nodeID: String = "" @Published var network: String? @@ -19,7 +19,8 @@ class SettingsViewModel: ObservableObject { @Published var isStatusFinished: Bool = false let keyClient: KeyClient - init(keyClient: KeyClient = .live) { + init(appState: Binding, keyClient: KeyClient = .live) { + _appState = appState self.keyClient = keyClient } @@ -47,47 +48,33 @@ class SettingsViewModel: ObservableObject { } func delete() { - do { - try LightningNodeService.shared.stop() - try LightningNodeService.shared.deleteWallet() - try KeyClient.live.deleteNetwork() - try KeyClient.live.deleteEsplora() - try LightningNodeService.shared.deleteDocuments() - self.isOnboarding = true - } catch let error as NodeError { - let errorString = handleNodeError(error) - DispatchQueue.main.async { - self.nodeIDError = .init(title: errorString.title, detail: errorString.detail) - } - } catch { - DispatchQueue.main.async { - self.nodeIDError = .init( - title: "Unexpected error", - detail: error.localizedDescription - ) + if network != nil { + do { + DispatchQueue.main.async { + self.appState = .loading + } + try LightningNodeService.shared.stop() + try LightningNodeService.shared.deleteDocuments(network: network!) + try LightningNodeService.shared.deleteWallet() + try KeyClient.live.deleteNetwork() + try KeyClient.live.deleteEsplora() + DispatchQueue.main.async { + self.appState = .onboarding + } + } catch let error as NodeError { + let errorString = handleNodeError(error) + DispatchQueue.main.async { + self.nodeIDError = .init(title: errorString.title, detail: errorString.detail) + } + } catch { + DispatchQueue.main.async { + self.appState = .error + } } + } else { + debugPrint("No Network found, so not deleting") } - } - func onboarding() { - do { - try LightningNodeService.shared.stop() - try KeyClient.live.deleteNetwork() - try KeyClient.live.deleteEsplora() - self.isOnboarding = true - } catch let error as NodeError { - let errorString = handleNodeError(error) - DispatchQueue.main.async { - self.nodeIDError = .init(title: errorString.title, detail: errorString.detail) - } - } catch { - DispatchQueue.main.async { - self.nodeIDError = .init( - title: "Unexpected error", - detail: error.localizedDescription - ) - } - } } func getNetwork() { From d70c3f18f1b347662b2103a5929531a18858e268 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:53:06 +0000 Subject: [PATCH 10/12] Delete network specific files --- .../LightningNodeService.swift | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift b/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift index 063bd478..3430d309 100644 --- a/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift +++ b/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift @@ -255,7 +255,25 @@ extension LightningNodeService { } extension LightningNodeService { - func deleteDocuments() throws { - try FileManager.default.deleteAllContentsInDocumentsDirectory() + func deleteDocuments(network: String) throws { + + let documentsPath = FileManager.default.getDocumentsDirectoryPath() + let networkURL = URL(fileURLWithPath: documentsPath) + .appendingPathComponent(network.description) + + guard FileManager.default.fileExists(atPath: networkURL.path()) else { + debugPrint("No files or folders to delete in: \(networkURL.path())") + return + } + + let contents = try FileManager.default.contentsOfDirectory( + at: networkURL, + includingPropertiesForKeys: nil, + options: [] + ) + + for fileURL in contents { + try FileManager.default.removeItem(at: fileURL) + } } } From 0771fc6869e2d9fdd3b20e1ece45d68d06de41a2 Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:22:47 +0000 Subject: [PATCH 11/12] Fix node not running throwing when deleting --- .../LightningNodeService.swift | 22 ++---------- .../Profile/SettingsViewModel.swift | 35 +++++++++---------- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift b/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift index 0118e193..fd65a8a9 100644 --- a/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift +++ b/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift @@ -266,25 +266,7 @@ extension LightningNodeService { } extension LightningNodeService { - func deleteDocuments(network: String) throws { - - let documentsPath = FileManager.default.getDocumentsDirectoryPath() - let networkURL = URL(fileURLWithPath: documentsPath) - .appendingPathComponent(network.description) - - guard FileManager.default.fileExists(atPath: networkURL.path()) else { - debugPrint("No files or folders to delete in: \(networkURL.path())") - return - } - - let contents = try FileManager.default.contentsOfDirectory( - at: networkURL, - includingPropertiesForKeys: nil, - options: [] - ) - - for fileURL in contents { - try FileManager.default.removeItem(at: fileURL) - } + func deleteDocuments() throws { + try FileManager.default.deleteAllContentsInDocumentsDirectory() } } diff --git a/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift b/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift index 7c0b5672..cbe0cb22 100644 --- a/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift +++ b/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift @@ -48,33 +48,30 @@ class SettingsViewModel: ObservableObject { } func delete() { - if network != nil { - do { - DispatchQueue.main.async { - self.appState = .loading - } + do { + if LightningNodeService.shared.status().isRunning { try LightningNodeService.shared.stop() - try LightningNodeService.shared.deleteDocuments(network: network!) - try LightningNodeService.shared.deleteWallet() - try KeyClient.live.deleteNetwork() - try KeyClient.live.deleteEsplora() - DispatchQueue.main.async { - self.appState = .onboarding - } - } catch let error as NodeError { - let errorString = handleNodeError(error) + } + try LightningNodeService.shared.deleteDocuments() + try LightningNodeService.shared.deleteWallet() + try KeyClient.live.deleteNetwork() + try KeyClient.live.deleteEsplora() + + DispatchQueue.main.async { + self.appState = .onboarding + } + } catch let error { + if let nodeError = error as? NodeError { + let errorString = handleNodeError(nodeError) DispatchQueue.main.async { self.nodeIDError = .init(title: errorString.title, detail: errorString.detail) } - } catch { + } else { DispatchQueue.main.async { - self.appState = .error + self.nodeIDError = .init(title: "Error", detail: error.localizedDescription) } } - } else { - debugPrint("No Network found, so not deleting") } - } func getNetwork() { From 6efca82c37a80e862fa90dea889fa46f8e5c8b5c Mon Sep 17 00:00:00 2001 From: Daniel Nordh <3393669+danielnordh@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:31:13 +0000 Subject: [PATCH 12/12] Coderabbit suggestions --- LDKNodeMonday/App/LDKNodeMondayApp.swift | 10 +--------- .../View Model/Profile/SettingsViewModel.swift | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/LDKNodeMonday/App/LDKNodeMondayApp.swift b/LDKNodeMonday/App/LDKNodeMondayApp.swift index be6e1d6e..b2b28f6b 100644 --- a/LDKNodeMonday/App/LDKNodeMondayApp.swift +++ b/LDKNodeMonday/App/LDKNodeMondayApp.swift @@ -45,15 +45,7 @@ struct LDKNodeMondayApp: App { func start() async { var backupInfo: BackupInfo? - do { - backupInfo = try KeyClient.live.getBackupInfo() - } catch let error { - debugPrint(error) // TODO: Show error on relevant screen, unless this is thrown if no seed has been saved - await MainActor.run { - self.appError = error - self.appState = .error - } - } + backupInfo = try? KeyClient.live.getBackupInfo() if backupInfo != nil { do { diff --git a/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift b/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift index cbe0cb22..062b5ebc 100644 --- a/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift +++ b/LDKNodeMonday/View Model/Profile/SettingsViewModel.swift @@ -54,8 +54,8 @@ class SettingsViewModel: ObservableObject { } try LightningNodeService.shared.deleteDocuments() try LightningNodeService.shared.deleteWallet() - try KeyClient.live.deleteNetwork() - try KeyClient.live.deleteEsplora() + try self.keyClient.deleteNetwork() + try self.keyClient.deleteEsplora() DispatchQueue.main.async { self.appState = .onboarding