From f64df7c5e55855dd7791af1ad95557c52ba367d2 Mon Sep 17 00:00:00 2001 From: Lain Iwakura Date: Thu, 7 Aug 2025 01:02:07 +0300 Subject: [PATCH] =?UTF-8?q?=D1=83=D1=80=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=85=D0=B2=D0=B0=D1=82=20=D0=BA=D1=80=D0=B0=D1=88=D0=B5=D0=B9?= =?UTF-8?q?=20=D0=B8=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BE=D0=B1=20=D1=83=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B9=D1=81=D1=82=D0=B2=D0=B5=20=D0=B9=D0=B5=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MobileMkch/CrashHandler.swift | 77 ++++++++++++++++++++++++++++++++++ MobileMkch/MobileMkchApp.swift | 17 +++++--- MobileMkch/SettingsView.swift | 69 ++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 MobileMkch/CrashHandler.swift diff --git a/MobileMkch/CrashHandler.swift b/MobileMkch/CrashHandler.swift new file mode 100644 index 0000000..59dee00 --- /dev/null +++ b/MobileMkch/CrashHandler.swift @@ -0,0 +1,77 @@ +import SwiftUI +import Foundation + +class CrashHandler: ObservableObject { + static let shared = CrashHandler() + @Published var hasCrashed = false + @Published var crashMessage = "" + + private init() { + setupCrashHandler() + } + + private func setupCrashHandler() { + NSSetUncaughtExceptionHandler { exception in + DispatchQueue.main.async { + CrashHandler.shared.hasCrashed = true + CrashHandler.shared.crashMessage = exception.reason ?? "idk че произошло\nПерезайди и скинь скрин из настроек (самый низ) (ну и че ты делал до краша)" + } + } + + signal(SIGABRT) { _ in + DispatchQueue.main.async { + CrashHandler.shared.hasCrashed = true + CrashHandler.shared.crashMessage = "Похоже... приложение упало..? Ты попал на ТЕРРИТОРИИ SIGABRT\nПерезайди и скинь скрин из настроек (самый низ)" + } + } + } + + func triggerTestCrash() { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.hasCrashed = true + self.crashMessage = "Похоже... приложение упало..? Ты попал на НЕИЗВЕДАННЫЕ ТЕРРИТОРИИ\nПерезайди и скинь скрин из настроек (самый низ)" + } + } +} + +struct CrashScreen: View { + @ObservedObject var crashHandler = CrashHandler.shared + + var body: some View { + VStack(spacing: 30) { + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 60)) + .foregroundColor(.red) + + Text("Произошла ошибка") + .font(.largeTitle) + .fontWeight(.bold) + + Text(crashHandler.crashMessage) + .font(.body) + .multilineTextAlignment(.center) + .padding(.horizontal) + + VStack(spacing: 15) { + Text("Рекомендуется:") + .font(.headline) + + Text("Закрыть приложение") + Text("Открыть заново") + Text("Если проблема повторяется - переустановить") + } + .font(.body) + .foregroundColor(.secondary) + + Spacer() + + Button("окек") { + exit(0) + } + .buttonStyle(.borderedProminent) + .foregroundColor(.red) + } + .padding() + .background(Color(.systemBackground)) + } +} \ No newline at end of file diff --git a/MobileMkch/MobileMkchApp.swift b/MobileMkch/MobileMkchApp.swift index a21d4da..9fc20ea 100644 --- a/MobileMkch/MobileMkchApp.swift +++ b/MobileMkch/MobileMkchApp.swift @@ -11,15 +11,22 @@ import SwiftUI struct MobileMkchApp: App { @StateObject private var settings = Settings() @StateObject private var apiClient = APIClient() + @StateObject private var crashHandler = CrashHandler.shared var body: some Scene { WindowGroup { - NavigationView { - BoardsView() - .environmentObject(settings) - .environmentObject(apiClient) + Group { + if crashHandler.hasCrashed { + CrashScreen() + } else { + NavigationView { + BoardsView() + .environmentObject(settings) + .environmentObject(apiClient) + } + .preferredColorScheme(settings.theme == "dark" ? .dark : .light) + } } - .preferredColorScheme(settings.theme == "dark" ? .dark : .light) } } } diff --git a/MobileMkch/SettingsView.swift b/MobileMkch/SettingsView.swift index 40371d9..c6d6679 100644 --- a/MobileMkch/SettingsView.swift +++ b/MobileMkch/SettingsView.swift @@ -1,5 +1,6 @@ import SwiftUI import Combine +import Darwin struct SettingsView: View { @EnvironmentObject var settings: Settings @@ -12,6 +13,8 @@ struct SettingsView: View { @State private var testPasscodeResult: String? @State private var isTestingKey = false @State private var isTestingPasscode = false + @State private var debugTapCount = 0 + @State private var showingDebugMenu = false var body: some View { NavigationView { @@ -140,6 +143,24 @@ struct SettingsView: View { showingInfo = true } } + + Section("Информация об устройстве") { + VStack(alignment: .leading, spacing: 4) { + Text("Устройство: \(getDeviceModel())") + Text("Система: \(UIDevice.current.systemName) \(UIDevice.current.systemVersion)") + Text("Тип: \(UIDevice.current.name.isEmpty ? "Не удалось определить, увы" : UIDevice.current.name)") + Text("Идентификатор: \(UIDevice.current.identifierForVendor?.uuidString ?? "Неизвестно")") + } + .font(.caption) + .foregroundColor(.secondary) + .onTapGesture { + debugTapCount += 1 + if debugTapCount >= 5 { + showingDebugMenu = true + debugTapCount = 0 + } + } + } } .navigationTitle("Настройки") .navigationBarTitleDisplayMode(.inline) @@ -153,6 +174,9 @@ struct SettingsView: View { .sheet(isPresented: $showingAbout) { AboutView() } + .sheet(isPresented: $showingDebugMenu) { + DebugMenuView() + } .alert("Информация о НЕОЖИДАНЫХ проблемах", isPresented: $showingInfo) { Button("Закрыть") { } } message: { @@ -198,6 +222,18 @@ struct SettingsView: View { } } } + + private func getDeviceModel() -> String { + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let identifier = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + + return identifier + } } struct AboutView: View { @@ -237,6 +273,39 @@ struct AboutView: View { } } +struct DebugMenuView: View { + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationView { + VStack(spacing: 20) { + Text("Debug Menu") + .font(.largeTitle) + .fontWeight(.bold) + + VStack(spacing: 15) { + Button("Тест краша") { + CrashHandler.shared.triggerTestCrash() + dismiss() + } + .buttonStyle(.borderedProminent) + .foregroundColor(.red) + } + + Spacer() + + Button("Закрыть") { + dismiss() + } + .buttonStyle(.bordered) + } + .padding() + .navigationTitle("Debug") + .navigationBarTitleDisplayMode(.inline) + } + } +} + #Preview { SettingsView() .environmentObject(Settings())