diff --git a/MobileWallet.xcodeproj/project.pbxproj b/MobileWallet.xcodeproj/project.pbxproj index 378026b0..1c3dbff6 100644 --- a/MobileWallet.xcodeproj/project.pbxproj +++ b/MobileWallet.xcodeproj/project.pbxproj @@ -4005,7 +4005,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.25.1; + MARKETING_VERSION = 0.25.2; PRODUCT_BUNDLE_IDENTIFIER = com.tari.wallet; PRODUCT_NAME = "Tari Aurora"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4038,7 +4038,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.25.1; + MARKETING_VERSION = 0.25.2; PRODUCT_BUNDLE_IDENTIFIER = com.tari.wallet; PRODUCT_NAME = "Tari Aurora"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/MobileWallet/Common/Managers/BugReportService.swift b/MobileWallet/Common/Managers/BugReportService.swift index 2b52c0c3..2b8ddc51 100644 --- a/MobileWallet/Common/Managers/BugReportService.swift +++ b/MobileWallet/Common/Managers/BugReportService.swift @@ -44,20 +44,25 @@ import Sentry final class BugReportService { private let workingDirectory = FileManager.default.temporaryDirectory.appendingPathComponent("BugReport") + private let filename = "BugReport.zip" // MARK: - Actions func send(name: String, email: String, message: String) async throws { try clearWorkingDirectory() try createWoringDirectory() - let attachementURL = try await prepareAttachement() + let attachementURL = try await prepareAttachement(zipName: filename) sendBugReport(name: name, email: email, message: message, attachementURL: attachementURL) try clearWorkingDirectory() } - private func prepareAttachement() async throws -> URL { + func prepareLogsURL(name: String) async throws -> URL { + try clearWorkingDirectory() + try createWoringDirectory() + return try await prepareAttachement(zipName: name) + } - let zipName = "BugReport.zip" + private func prepareAttachement(zipName: String) async throws -> URL { let logsURLs = try Tari.shared.logsURLs.prefix(5) let localFileURL = workingDirectory.appendingPathComponent(zipName) @@ -74,7 +79,7 @@ final class BugReportService { } } - private func clearWorkingDirectory() throws { + func clearWorkingDirectory() throws { guard FileManager.default.fileExists(atPath: workingDirectory.path) else { return } try FileManager.default.removeItem(at: workingDirectory) } diff --git a/MobileWallet/Libraries/TariLib/Core/Tari.swift b/MobileWallet/Libraries/TariLib/Core/Tari.swift index 6c3d69e4..f9808d5b 100644 --- a/MobileWallet/Libraries/TariLib/Core/Tari.swift +++ b/MobileWallet/Libraries/TariLib/Core/Tari.swift @@ -141,7 +141,9 @@ final class Tari: MainServiceable { private func setupCallbacks() { - Publishers.CombineLatest(connectionMonitor.$baseNodeConnection, connectionMonitor.$syncStatus) + Publishers.CombineLatest(connectionMonitor.$baseNodeConnection.removeDuplicates(), connectionMonitor.$syncStatus.removeDuplicates()) + .dropFirst() + .filter { [weak self] _, _ in self?.isWalletConnected == true } .filter { $0 == .offline || $1 == .failed } .sink { [weak self] _, _ in try? self?.switchBaseNode() } .store(in: &cancellables) @@ -241,7 +243,7 @@ final class Tari: MainServiceable { let logFilePath = logFilePath Logger.log(message: "Log Path: \(logFilePath)", domain: .general, level: .info) - let logVerbosity: Int32 = TariSettings.shared.environment == .debug ? 11 : 2 + let logVerbosity: Int32 = TariSettings.shared.environment == .debug ? 11 : 4 try walletManager.connectWallet(commsConfig: commsConfig, logFilePath: logFilePath, seedWords: walletSeedWords, passphrase: passphrase, networkName: selectedNetwork.name, logVerbosity: logVerbosity) resetServices() diff --git a/MobileWallet/Libraries/TariLib/Wrappers/Utils/Network/TariNetwork.swift b/MobileWallet/Libraries/TariLib/Wrappers/Utils/Network/TariNetwork.swift index a8da41f8..b89fba5d 100644 --- a/MobileWallet/Libraries/TariLib/Wrappers/Utils/Network/TariNetwork.swift +++ b/MobileWallet/Libraries/TariLib/Wrappers/Utils/Network/TariNetwork.swift @@ -98,8 +98,11 @@ extension TariNetwork { isMainNet: false, rawBaseNodes: [ "NextNet 1": "0cff11dff44458bfea3e39444d440e54260746ff2a5ce6a6c3f7355decff2167::/ip4/54.195.217.107/tcp/18189", - "NextNet 2": "0cff11dff44458bfea3e39444d440e54260746ff2a5ce6a6c3f7355decff2167::/onion3/h6oj2cusgtaxo63zbfw2wjir4mltkqzz4jquoak2i5mvgyszaieowwad:18141", - "NextNet 3": "4c236de788e803ef9615f72a4d973cf3f8a9b83c9d2fb176cbaf65c1b0442572::/onion3/3jtk3e2ud3zqtbrq36sw6ata6u5epkjmqgr5tfuemcfpyhisrzkgbtyd:18141" + "NextNet 2": "0cff11dff44458bfea3e39444d440e54260746ff2a5ce6a6c3f7355decff2167::/onion3/cmdlunlzessyz7snnat6ktxwmcl7bubvfzneb7ljva4w4izcmlqyc2id:18141", + "NextNet 3": "2caf4be53523ecf2b71ca23db33f7df590fd41792b2d14c2bb0bbebe1b2c4b52::/ip4/34.254.114.227/tcp/18189", + "NextNet 4": "2caf4be53523ecf2b71ca23db33f7df590fd41792b2d14c2bb0bbebe1b2c4b52::/onion3/wi2teiwyyq33jwblcuy7anmja6d7iyuciyrzbwumvldeiw73d6ce5ryd:18141", + "NextNet 5": "4c236de788e803ef9615f72a4d973cf3f8a9b83c9d2fb176cbaf65c1b0442572::/ip4/52.210.35.123/tcp/18189", + "NextNet 6": "4c236de788e803ef9615f72a4d973cf3f8a9b83c9d2fb176cbaf65c1b0442572::/onion3/7gwfakr7ko5uo3fl3yz3fsjc7elccbzter5botggodrmmwi2exm3vbid:18141" ] ) } diff --git a/MobileWallet/Screens/AdvancedSettings/SelectBaseNode/SelectBaseNodeModel.swift b/MobileWallet/Screens/AdvancedSettings/SelectBaseNode/SelectBaseNodeModel.swift index f02ad0ae..155826aa 100644 --- a/MobileWallet/Screens/AdvancedSettings/SelectBaseNode/SelectBaseNodeModel.swift +++ b/MobileWallet/Screens/AdvancedSettings/SelectBaseNode/SelectBaseNodeModel.swift @@ -40,11 +40,16 @@ final class SelectBaseNodeModel { - struct NodeModel: Hashable { + struct NodeModel: Hashable, Identifiable { + let id: UUID = UUID() let title: String let subtitle: String let isSelected: Bool let canBeRemoved: Bool + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } } // MARK: - View Model diff --git a/MobileWallet/Screens/Debug/Logs/List/LogsListModel.swift b/MobileWallet/Screens/Debug/Logs/List/LogsListModel.swift index 8170b9a6..8a16b42d 100644 --- a/MobileWallet/Screens/Debug/Logs/List/LogsListModel.swift +++ b/MobileWallet/Screens/Debug/Logs/List/LogsListModel.swift @@ -40,15 +40,21 @@ final class LogsListModel { + enum Action { + case share(url: URL) + } + // MARK: - View Model @Published private(set) var logTitles: [String] = [] @Published private(set) var selectedRowFileURL: URL? + @Published private(set) var action: Action? @Published private(set) var errorMessage: MessageModel? // MARK: - Properties private var logsURLs: [URL] = [] + private let bugReportService = BugReportService() // MARK: - Actions @@ -65,6 +71,21 @@ final class LogsListModel { selectedRowFileURL = logsURLs[row] } + func requestLogsFile() { + Task { + do { + let url = try await bugReportService.prepareLogsURL(name: "logs.zip") + action = .share(url: url) + } catch { + errorMessage = ErrorMessageManager.errorModel(forError: error) + } + } + } + + func removeTempFiles() { + try? bugReportService.clearWorkingDirectory() + } + // MARK: - Handlers private func title(fileURL: URL) throws -> String { diff --git a/MobileWallet/Screens/Debug/Logs/List/LogsListView.swift b/MobileWallet/Screens/Debug/Logs/List/LogsListView.swift index c7831570..0dca6fd4 100644 --- a/MobileWallet/Screens/Debug/Logs/List/LogsListView.swift +++ b/MobileWallet/Screens/Debug/Logs/List/LogsListView.swift @@ -52,6 +52,10 @@ final class LogsListView: BaseNavigationContentView { return view }() + // MARK: - Properties + + var onExportButtonTap: (() -> Void)? + // MARK: - Initialisers override init() { @@ -69,6 +73,9 @@ final class LogsListView: BaseNavigationContentView { private func setupViews() { navigationBar.title = localized("debug.logs.list.title") navigationBar.backButtonType = .close + navigationBar.update(rightButton: NavigationBar.ButtonModel(title: localized("debug.logs.list.button.export"), callback: { [weak self] in + self?.onExportButtonTap?() + })) } private func setupConstraints() { diff --git a/MobileWallet/Screens/Debug/Logs/List/LogsListViewController.swift b/MobileWallet/Screens/Debug/Logs/List/LogsListViewController.swift index d5c80020..19b2eeb3 100644 --- a/MobileWallet/Screens/Debug/Logs/List/LogsListViewController.swift +++ b/MobileWallet/Screens/Debug/Logs/List/LogsListViewController.swift @@ -91,11 +91,22 @@ final class LogsListViewController: UIViewController { .sink { [weak self] in self?.moveToLogDetails(url: $0) } .store(in: &cancellables) + model.$action + .compactMap { $0 } + .receive(on: DispatchQueue.main) + .sink { [weak self] in self?.handle(action: $0) } + .store(in: &cancellables) + model.$errorMessage .compactMap { $0 } + .receive(on: DispatchQueue.main) .sink { PopUpPresenter.show(message: $0) } .store(in: &cancellables) + mainView.onExportButtonTap = { [weak self] in + self?.model.requestLogsFile() + } + tableDataSource = UITableViewDiffableDataSource(tableView: mainView.tableView) { tableView, indexPath, model in let cell = tableView.dequeueReusableCell(type: LogsListCell.self, indexPath: indexPath) cell.update(title: model) @@ -121,6 +132,22 @@ final class LogsListViewController: UIViewController { let controller = LogConstructor.buildScene(fileURL: url) navigationController?.pushViewController(controller, animated: true) } + + private func showShareDialog(url: URL) { + let controller = UIActivityViewController(activityItems: [url], applicationActivities: nil) + controller.popoverPresentationController?.sourceView = mainView.navigationBar + controller.completionWithItemsHandler = { [weak self] _, _, _, _ in self?.model.removeTempFiles() } + present(controller, animated: true) + } + + // MARK: - Handlers + + private func handle(action: LogsListModel.Action) { + switch action { + case let .share(url): + showShareDialog(url: url) + } + } } extension LogsListViewController: UITableViewDelegate { diff --git a/MobileWallet/Screens/Debug/UIViewController+DebugMenu.swift b/MobileWallet/Screens/Debug/UIViewController+DebugMenu.swift index bb59104c..b2ec6762 100644 --- a/MobileWallet/Screens/Debug/UIViewController+DebugMenu.swift +++ b/MobileWallet/Screens/Debug/UIViewController+DebugMenu.swift @@ -46,7 +46,7 @@ extension UIViewController { open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { super.motionEnded(motion, with: event) - guard motion == .motionShake else { return } + guard motion == .motionShake, self == UIApplication.shared.topController else { return } handleShakeGesture() } diff --git a/MobileWallet/en.lproj/Localizable.strings b/MobileWallet/en.lproj/Localizable.strings index e89ed5be..baf133d5 100644 --- a/MobileWallet/en.lproj/Localizable.strings +++ b/MobileWallet/en.lproj/Localizable.strings @@ -656,6 +656,7 @@ "debug.popup.options.bug_report"= "Report a bug"; "debug.popup.options.connection_status"= "View connection status"; "debug.logs.list.title" = "Debug Logs"; +"debug.logs.list.button.export" = "Export"; "debug.logs.list.error.message.unable_to_load" = "Unable to load log files. Please try again later or send us a bug report."; "debug.logs.details.error.message.cant_open_file" = "Unable to open file. Please try again later."; "debug.logs.details.filters.title" = "Filter";