From 5941dc12d2ae8924e7c2379e1ed11ef093b7998f Mon Sep 17 00:00:00 2001 From: neoarz Date: Mon, 31 Mar 2025 20:11:17 -0400 Subject: [PATCH 1/4] commit number 1 --- StikJIT/StikJITApp.swift | 122 ++++++++++++++-------- StikJIT/Views/Custom Error View.swift | 144 ++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 41 deletions(-) create mode 100644 StikJIT/Views/Custom Error View.swift diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index f250755..f5de8c9 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -232,18 +232,25 @@ struct HeartbeatApp: App { alert_string = "[Download DDI Error]: " + result show_alert = true } - } - } } - - } - .alert(alert_title, isPresented: $show_alert) { - Button("OK", role: .cancel) { } - } message: { - Text(alert_title) } + .overlay( + ZStack { + if show_alert { + CustomErrorView( + title: alert_title, + message: alert_string, + onDismiss: { + show_alert = false + }, + showButton: true, + primaryButtonText: "OK" + ) + } + } + ) } } } @@ -430,16 +437,15 @@ func startHeartbeatInBackground() { print("Error: \(message ?? "") (Code: \(result))") DispatchQueue.main.async { - if let mainWindow = UIApplication.shared.windows.last { - let alert = UIAlertController(title: "Heartbeat Error", message: "\(message ?? "") (\(result))", preferredStyle: .alert) - - let tryAgainAction = UIAlertAction(title: "Try Again", style: .default) { _ in + showAlert( + title: "Heartbeat Error", + message: "Failed to connect to Heartbeat (\(result))", + showOk: false, + showTryAgain: true + ) { shouldTryAgain in + if shouldTryAgain { startHeartbeatInBackground() } - - alert.addAction(tryAgainAction) - - mainWindow.rootViewController?.present(alert, animated: true, completion: nil) } } } @@ -499,34 +505,68 @@ struct LoadingView: View { public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, completion: @escaping (Bool) -> Void) { DispatchQueue.main.async { - if let mainWindow = UIApplication.shared.windows.last { - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - - if showOk, !showTryAgain { - let okAction = UIAlertAction(title: "OK", style: .default) { _ in - completion(true) - } - - alert.addAction(okAction) - } else if !showTryAgain { - completion(false) - } - - if showTryAgain { - let tryAgainAction = UIAlertAction(title: "Try Again", style: .default) { _ in - completion(true) - } - - alert.addAction(tryAgainAction) - - let okAction = UIAlertAction(title: "OK", style: .cancel) { _ in + let rootViewController = UIApplication.shared.windows.last?.rootViewController + + if showTryAgain { + // Configure with "Try Again" button only (no Cancel button) + let customErrorView = CustomErrorView( + title: title, + message: message, + onDismiss: { + // Called when tapped outside completion(false) + }, + showButton: true, + primaryButtonText: "Try Again", + onPrimaryButtonTap: { + // Try Again was tapped + completion(true) } - - alert.addAction(okAction) - } + ) - mainWindow.rootViewController?.present(alert, animated: true, completion: nil) + let hostingController = UIHostingController(rootView: customErrorView) + hostingController.modalPresentationStyle = .overFullScreen + hostingController.modalTransitionStyle = .crossDissolve + hostingController.view.backgroundColor = .clear + rootViewController?.present(hostingController, animated: true) + } else if showOk { + // Configure with just "OK" button + let customErrorView = CustomErrorView( + title: title, + message: message, + onDismiss: { + // Called when tapped outside + completion(true) + }, + showButton: true, + primaryButtonText: "OK", + onPrimaryButtonTap: { + // OK was tapped + completion(true) + } + ) + + let hostingController = UIHostingController(rootView: customErrorView) + hostingController.modalPresentationStyle = .overFullScreen + hostingController.modalTransitionStyle = .crossDissolve + hostingController.view.backgroundColor = .clear + rootViewController?.present(hostingController, animated: true) + } else { + // No buttons case + let customErrorView = CustomErrorView( + title: title, + message: message, + onDismiss: { + completion(false) + }, + showButton: false + ) + + let hostingController = UIHostingController(rootView: customErrorView) + hostingController.modalPresentationStyle = .overFullScreen + hostingController.modalTransitionStyle = .crossDissolve + hostingController.view.backgroundColor = .clear + rootViewController?.present(hostingController, animated: true) } } } diff --git a/StikJIT/Views/Custom Error View.swift b/StikJIT/Views/Custom Error View.swift new file mode 100644 index 0000000..df648d4 --- /dev/null +++ b/StikJIT/Views/Custom Error View.swift @@ -0,0 +1,144 @@ +import SwiftUI + +struct CustomErrorView: View { + var title: String + var message: String + var onDismiss: () -> Void + var showButton: Bool = true + @State private var opacity: Double = 0 + @State private var scale: CGFloat = 0.8 + var primaryButtonText: String = "OK" + var secondaryButtonText: String = "Cancel" + var onPrimaryButtonTap: (() -> Void)? = nil + var onSecondaryButtonTap: (() -> Void)? = nil + var showSecondaryButton: Bool = false + + var body: some View { + ZStack { + // Background overlay + Color.black.opacity(0.6) + .edgesIgnoringSafeArea(.all) + .opacity(opacity) + .onTapGesture { + if showButton { + dismissWithAnimation() + } + } + + // Error card - made more compact + VStack(spacing: 12) { + // Error icon - smaller + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 32)) + .foregroundColor(.red.opacity(0.9)) + .padding(.top, 8) + + // Title - slightly smaller + Text(title) + .font(.system(size: 18, weight: .bold, design: .rounded)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + + // Divider + Rectangle() + .frame(height: 1) + .foregroundColor(.white.opacity(0.2)) + .padding(.horizontal, 12) + + // Message - slightly smaller + Text(message) + .font(.system(size: 15, design: .rounded)) + .foregroundColor(.white.opacity(0.9)) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .padding(.horizontal, 12) + + // Restart instructions when button is hidden + if !showButton { + Text("Please connect to WireGuard VPN and restart the app.") + .font(.system(size: 15, weight: .medium, design: .rounded)) + .foregroundColor(.white.opacity(0.9)) + .multilineTextAlignment(.center) + .padding(.top, 4) + .padding(.horizontal, 12) + } + + // Dismiss button (only shown when showButton is true) + if showButton { + VStack(spacing: 6) { + Button(action: { + dismissWithAnimation() + onPrimaryButtonTap?() // Call primary action if provided + }) { + Text(primaryButtonText) + .font(.system(size: 16, weight: .semibold, design: .rounded)) + .foregroundColor(.black) + .frame(height: 38) + .frame(maxWidth: .infinity) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color.white) + ) + } + + if showSecondaryButton { + Button(action: { + dismissWithAnimation() + onSecondaryButtonTap?() // Call secondary action if provided + }) { + Text(secondaryButtonText) + .font(.system(size: 16, weight: .medium, design: .rounded)) + .foregroundColor(.white) + .frame(height: 38) + .frame(maxWidth: .infinity) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color.gray.opacity(0.3)) + ) + } + } + } + .padding(.horizontal, 12) + .padding(.bottom, 12) + .padding(.top, 6) + } else { + Spacer() + .frame(height: 12) + } + } + // Made the card smaller overall + .frame(width: min(UIScreen.main.bounds.width - 80, 300)) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(Color(UIColor.systemGray6).opacity(0.95)) + .overlay( + RoundedRectangle(cornerRadius: 16) + .stroke(Color.white.opacity(0.2), lineWidth: 1) + ) + ) + .shadow(color: Color.black.opacity(0.25), radius: 16, x: 0, y: 8) + .scaleEffect(scale) + .opacity(opacity) + } + .onAppear { + // Made animation faster (0.25s instead of 0.4s) + withAnimation(.spring(response: 0.25, dampingFraction: 0.7)) { + opacity = 1 + scale = 1 + } + } + } + + private func dismissWithAnimation() { + // Made dismiss animation faster (0.15s instead of 0.2s) + withAnimation(.easeOut(duration: 0.15)) { + opacity = 0 + scale = 0.8 + } + + // Delay the actual dismissal to allow the animation to complete + DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { + onDismiss() + } + } +} \ No newline at end of file From ba73b5b0f1cedc1a10d19bdde4a2088a68ff98a4 Mon Sep 17 00:00:00 2001 From: neoarz Date: Mon, 31 Mar 2025 20:13:03 -0400 Subject: [PATCH 2/4] remove old text --- StikJIT/Views/Custom Error View.swift | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/StikJIT/Views/Custom Error View.swift b/StikJIT/Views/Custom Error View.swift index df648d4..f7f6cbf 100644 --- a/StikJIT/Views/Custom Error View.swift +++ b/StikJIT/Views/Custom Error View.swift @@ -25,9 +25,9 @@ struct CustomErrorView: View { } } - // Error card - made more compact + // Card itself VStack(spacing: 12) { - // Error icon - smaller + // Error icon Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 32)) .foregroundColor(.red.opacity(0.9)) @@ -45,7 +45,7 @@ struct CustomErrorView: View { .foregroundColor(.white.opacity(0.2)) .padding(.horizontal, 12) - // Message - slightly smaller + // Message Text(message) .font(.system(size: 15, design: .rounded)) .foregroundColor(.white.opacity(0.9)) @@ -53,15 +53,6 @@ struct CustomErrorView: View { .fixedSize(horizontal: false, vertical: true) .padding(.horizontal, 12) - // Restart instructions when button is hidden - if !showButton { - Text("Please connect to WireGuard VPN and restart the app.") - .font(.system(size: 15, weight: .medium, design: .rounded)) - .foregroundColor(.white.opacity(0.9)) - .multilineTextAlignment(.center) - .padding(.top, 4) - .padding(.horizontal, 12) - } // Dismiss button (only shown when showButton is true) if showButton { From 621da2bc7461705d72226acdebbac55e3f43b9b5 Mon Sep 17 00:00:00 2001 From: neoarz Date: Mon, 31 Mar 2025 20:16:48 -0400 Subject: [PATCH 3/4] add messages to console logs --- StikJIT/Views/ConsoleLogsView.swift | 36 +++++++++++++++------------ StikJIT/Views/Custom Error View.swift | 6 ++--- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index 9529ffe..77180d4 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -14,9 +14,8 @@ struct ConsoleLogsView: View { @State private var autoScroll = true @State private var scrollView: ScrollViewProxy? = nil - // Alert handlin - @State private var showingExportAlert = false - @State private var showingCopyAlert = false + // Alert handling + @State private var showingCustomAlert = false @State private var alertMessage = "" @State private var alertTitle = "" @State private var isError = false @@ -119,13 +118,13 @@ struct ConsoleLogsView: View { alertTitle = "Logs Exported" alertMessage = "Logs have been saved to Files app in StikJIT folder." isError = false - showingExportAlert = true + showingCustomAlert = true } catch { // Set error alert variables and show the alert alertTitle = "Export Failed" alertMessage = "Failed to save logs: \(error.localizedDescription)" isError = true - showingExportAlert = true + showingCustomAlert = true } }) { HStack { @@ -166,7 +165,7 @@ struct ConsoleLogsView: View { alertTitle = "Logs Copied" alertMessage = "Logs have been copied to clipboard." isError = false - showingCopyAlert = true + showingCustomAlert = true }) { HStack { Text("Copy Logs") @@ -220,16 +219,21 @@ struct ConsoleLogsView: View { } } .navigationViewStyle(StackNavigationViewStyle()) - .alert(alertTitle, isPresented: $showingExportAlert) { - Button("OK", role: .cancel) { } - } message: { - Text(alertMessage) - } - .alert(alertTitle, isPresented: $showingCopyAlert) { - Button("OK", role: .cancel) { } - } message: { - Text(alertMessage) - } + .overlay( + ZStack { + if showingCustomAlert { + CustomErrorView( + title: alertTitle, + message: alertMessage, + onDismiss: { + showingCustomAlert = false + }, + showButton: true, + primaryButtonText: "OK" + ) + } + } + ) } // Creates an NSAttributedString that combines timestamp, type, and message diff --git a/StikJIT/Views/Custom Error View.swift b/StikJIT/Views/Custom Error View.swift index f7f6cbf..530f07b 100644 --- a/StikJIT/Views/Custom Error View.swift +++ b/StikJIT/Views/Custom Error View.swift @@ -112,7 +112,7 @@ struct CustomErrorView: View { .opacity(opacity) } .onAppear { - // Made animation faster (0.25s instead of 0.4s) + withAnimation(.spring(response: 0.25, dampingFraction: 0.7)) { opacity = 1 scale = 1 @@ -121,13 +121,13 @@ struct CustomErrorView: View { } private func dismissWithAnimation() { - // Made dismiss animation faster (0.15s instead of 0.2s) + withAnimation(.easeOut(duration: 0.15)) { opacity = 0 scale = 0.8 } - // Delay the actual dismissal to allow the animation to complete + DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { onDismiss() } From 830bfdb18f03fc8fff70f6a094d77db62e062ef5 Mon Sep 17 00:00:00 2001 From: neoarz Date: Mon, 31 Mar 2025 20:18:58 -0400 Subject: [PATCH 4/4] add good message as well for console logs --- StikJIT/Views/ConsoleLogsView.swift | 3 ++- StikJIT/Views/Custom Error View.swift | 30 ++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index 77180d4..d9299a8 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -229,7 +229,8 @@ struct ConsoleLogsView: View { showingCustomAlert = false }, showButton: true, - primaryButtonText: "OK" + primaryButtonText: "OK", + messageType: isError ? .error : .success ) } } diff --git a/StikJIT/Views/Custom Error View.swift b/StikJIT/Views/Custom Error View.swift index 530f07b..a781fc8 100644 --- a/StikJIT/Views/Custom Error View.swift +++ b/StikJIT/Views/Custom Error View.swift @@ -12,6 +12,13 @@ struct CustomErrorView: View { var onPrimaryButtonTap: (() -> Void)? = nil var onSecondaryButtonTap: (() -> Void)? = nil var showSecondaryButton: Bool = false + var messageType: MessageType = .error + + enum MessageType { + case error + case success + case info + } var body: some View { ZStack { @@ -27,11 +34,24 @@ struct CustomErrorView: View { // Card itself VStack(spacing: 12) { - // Error icon - Image(systemName: "exclamationmark.triangle.fill") - .font(.system(size: 32)) - .foregroundColor(.red.opacity(0.9)) - .padding(.top, 8) + // Icon based on messageType + switch messageType { + case .error: + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 32)) + .foregroundColor(.red.opacity(0.9)) + .padding(.top, 8) + case .success: + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 32)) + .foregroundColor(.green.opacity(0.9)) + .padding(.top, 8) + case .info: + Image(systemName: "info.circle.fill") + .font(.system(size: 32)) + .foregroundColor(.blue.opacity(0.9)) + .padding(.top, 8) + } // Title - slightly smaller Text(title)