From 0aca8c25c1ba69f13759c4df914446250c95c22e Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 24 Feb 2023 14:51:36 +0200 Subject: [PATCH] Fixes #614 - Add the user and device identifiers to rageshakes (#620) * Fixes #614 - Add the user and device identifiers to rageshakes * Add `base_bundle_identifier` to rageshakes --- .../Sources/Application/AppCoordinator.swift | 2 +- .../BugReport/BugReportCoordinator.swift | 5 ++++ .../BugReport/BugReportViewModel.swift | 15 ++++++++--- .../BugReport/View/BugReportScreen.swift | 6 +++++ .../Settings/SettingsScreenCoordinator.swift | 2 ++ .../Settings/SettingsScreenViewModel.swift | 2 +- .../Services/BugReport/BugReportService.swift | 18 ++++++++++--- .../BugReport/BugReportServiceProtocol.swift | 2 ++ .../Services/Session/MockUserSession.swift | 2 +- .../Services/Session/UserSession.swift | 2 +- .../Session/UserSessionProtocol.swift | 7 +++--- .../UserSessionFlowCoordinator.swift | 2 ++ .../UITests/UITestsAppCoordinator.swift | 4 +++ UnitTests/Sources/BugReportServiceTests.swift | 8 ++++-- .../Sources/BugReportViewModelTests.swift | 25 +++++++++++++------ 15 files changed, 79 insertions(+), 23 deletions(-) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 8575757da..e2ad4159b 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -235,7 +235,7 @@ class AppCoordinator: AppCoordinatorProtocol { let credentials = SoftLogoutCredentials(userId: userSession.userID, homeserverName: userSession.homeserver, userDisplayName: displayName, - deviceId: userSession.deviceId) + deviceId: userSession.deviceID) let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore) _ = await authenticationService.configure(for: userSession.homeserver) diff --git a/ElementX/Sources/Screens/BugReport/BugReportCoordinator.swift b/ElementX/Sources/Screens/BugReport/BugReportCoordinator.swift index 6e7e107ff..1bcd9c22c 100644 --- a/ElementX/Sources/Screens/BugReport/BugReportCoordinator.swift +++ b/ElementX/Sources/Screens/BugReport/BugReportCoordinator.swift @@ -23,6 +23,9 @@ enum BugReportCoordinatorResult { struct BugReportCoordinatorParameters { let bugReportService: BugReportServiceProtocol + let userID: String + let deviceID: String? + weak var userIndicatorController: UserIndicatorControllerProtocol? let screenshot: UIImage? let isModallyPresented: Bool @@ -38,6 +41,8 @@ final class BugReportCoordinator: CoordinatorProtocol { self.parameters = parameters viewModel = BugReportViewModel(bugReportService: parameters.bugReportService, + userID: parameters.userID, + deviceID: parameters.deviceID, screenshot: parameters.screenshot, isModallyPresented: parameters.isModallyPresented) } diff --git a/ElementX/Sources/Screens/BugReport/BugReportViewModel.swift b/ElementX/Sources/Screens/BugReport/BugReportViewModel.swift index ce731af96..a48013371 100644 --- a/ElementX/Sources/Screens/BugReport/BugReportViewModel.swift +++ b/ElementX/Sources/Screens/BugReport/BugReportViewModel.swift @@ -19,14 +19,21 @@ import SwiftUI typealias BugReportViewModelType = StateStoreViewModel class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol { - let bugReportService: BugReportServiceProtocol + private let bugReportService: BugReportServiceProtocol + private let userID: String + private let deviceID: String? var callback: ((BugReportViewModelAction) -> Void)? - + init(bugReportService: BugReportServiceProtocol, + userID: String, + deviceID: String?, screenshot: UIImage?, isModallyPresented: Bool) { self.bugReportService = bugReportService + self.userID = userID + self.deviceID = deviceID + let bindings = BugReportViewStateBindings(reportText: "", sendingLogsEnabled: true) super.init(initialViewState: BugReportViewState(screenshot: screenshot, bindings: bindings, @@ -61,7 +68,9 @@ class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol { try pngData?.write(to: imageURL) files.append(imageURL) } - let bugReport = BugReport(text: context.reportText, + let bugReport = BugReport(userID: userID, + deviceID: deviceID, + text: context.reportText, includeLogs: context.sendingLogsEnabled, includeCrashLog: true, githubLabels: [], diff --git a/ElementX/Sources/Screens/BugReport/View/BugReportScreen.swift b/ElementX/Sources/Screens/BugReport/View/BugReportScreen.swift index d2c883429..4e749af8a 100644 --- a/ElementX/Sources/Screens/BugReport/View/BugReportScreen.swift +++ b/ElementX/Sources/Screens/BugReport/View/BugReportScreen.swift @@ -173,12 +173,16 @@ struct BugReportScreen: View { struct BugReport_Previews: PreviewProvider { static let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), + userID: "@mock.client.com", + deviceID: nil, screenshot: nil, isModallyPresented: false) static var previews: some View { NavigationStack { BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(), + userID: "@mock.client.com", + deviceID: nil, screenshot: nil, isModallyPresented: false).context) .previewDisplayName("Without Screenshot") @@ -186,6 +190,8 @@ struct BugReport_Previews: PreviewProvider { NavigationStack { BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(), + userID: "@mock.client.com", + deviceID: nil, screenshot: Asset.Images.appLogo.image, isModallyPresented: false).context) .previewDisplayName("With Screenshot") diff --git a/ElementX/Sources/Screens/Settings/SettingsScreenCoordinator.swift b/ElementX/Sources/Screens/Settings/SettingsScreenCoordinator.swift index a280a2508..a7b088625 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreenCoordinator.swift @@ -78,6 +78,8 @@ final class SettingsScreenCoordinator: CoordinatorProtocol { private func presentBugReportScreen() { let params = BugReportCoordinatorParameters(bugReportService: parameters.bugReportService, + userID: parameters.userSession.userID, + deviceID: parameters.userSession.deviceID, userIndicatorController: parameters.userIndicatorController, screenshot: nil, isModallyPresented: false) diff --git a/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift b/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift index 82d22bb42..35f33e5f1 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift @@ -28,7 +28,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo self.userSession = userSession let bindings = SettingsScreenViewStateBindings(timelineStyle: ServiceLocator.shared.settings.timelineStyle) super.init(initialViewState: .init(bindings: bindings, - deviceID: userSession.deviceId, + deviceID: userSession.deviceID, userID: userSession.userID, showSessionVerificationSection: !(userSession.sessionVerificationController?.isVerified ?? false), showDeveloperOptions: ServiceLocator.shared.settings.canShowDeveloperOptions), diff --git a/ElementX/Sources/Services/BugReport/BugReportService.swift b/ElementX/Sources/Services/BugReport/BugReportService.swift index 47762f113..1f0299eb1 100644 --- a/ElementX/Sources/Services/BugReport/BugReportService.swift +++ b/ElementX/Sources/Services/BugReport/BugReportService.swift @@ -78,10 +78,19 @@ class BugReportService: NSObject, BugReportServiceProtocol { SentrySDK.crash() } - func submitBugReport(_ bugReport: BugReport, - progressListener: ProgressListener?) async throws -> SubmitBugReportResponse { - var params = [MultipartFormData(key: "text", type: .text(value: bugReport.text))] + // swiftlint:disable:next function_body_length + func submitBugReport(_ bugReport: BugReport, progressListener: ProgressListener?) async throws -> SubmitBugReportResponse { + var params = [ + MultipartFormData(key: "user_id", type: .text(value: bugReport.userID)), + MultipartFormData(key: "text", type: .text(value: bugReport.text)) + ] + + if let deviceID = bugReport.deviceID { + params.append(.init(key: "device_id", type: .text(value: deviceID))) + } + params.append(contentsOf: defaultParams) + for label in bugReport.githubLabels { params.append(MultipartFormData(key: "label", type: .text(value: label))) } @@ -155,7 +164,8 @@ class BugReportService: NSObject, BugReportServiceProtocol { MultipartFormData(key: "user_language", type: .text(value: Bundle.elementLanguage ?? "null")), MultipartFormData(key: "fallback_language", type: .text(value: Bundle.elementFallbackLanguage ?? "null")), MultipartFormData(key: "local_time", type: .text(value: localTime)), - MultipartFormData(key: "utc_time", type: .text(value: utcTime)) + MultipartFormData(key: "utc_time", type: .text(value: utcTime)), + MultipartFormData(key: "base_bundle_identifier", type: .text(value: InfoPlistReader.main.baseBundleIdentifier)) ] } diff --git a/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift b/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift index 9873ba9f4..e3ed76e96 100644 --- a/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift +++ b/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift @@ -18,6 +18,8 @@ import Foundation import UIKit struct BugReport { + let userID: String + let deviceID: String? let text: String let includeLogs: Bool let includeCrashLog: Bool diff --git a/ElementX/Sources/Services/Session/MockUserSession.swift b/ElementX/Sources/Services/Session/MockUserSession.swift index c7eccdf1a..334f90301 100644 --- a/ElementX/Sources/Services/Session/MockUserSession.swift +++ b/ElementX/Sources/Services/Session/MockUserSession.swift @@ -21,7 +21,7 @@ struct MockUserSession: UserSessionProtocol { let sessionVerificationController: SessionVerificationControllerProxyProtocol? = nil var userID: String { clientProxy.userID } var isSoftLogout: Bool { clientProxy.isSoftLogout } - var deviceId: String? { clientProxy.deviceId } + var deviceID: String? { clientProxy.deviceId } var homeserver: String { clientProxy.homeserver } let clientProxy: ClientProxyProtocol let mediaProvider: MediaProviderProtocol diff --git a/ElementX/Sources/Services/Session/UserSession.swift b/ElementX/Sources/Services/Session/UserSession.swift index 439ce0980..d1209e537 100644 --- a/ElementX/Sources/Services/Session/UserSession.swift +++ b/ElementX/Sources/Services/Session/UserSession.swift @@ -25,7 +25,7 @@ class UserSession: UserSessionProtocol { var userID: String { clientProxy.userID } var isSoftLogout: Bool { clientProxy.isSoftLogout } - var deviceId: String? { clientProxy.deviceId } + var deviceID: String? { clientProxy.deviceId } var homeserver: String { clientProxy.homeserver } let clientProxy: ClientProxyProtocol diff --git a/ElementX/Sources/Services/Session/UserSessionProtocol.swift b/ElementX/Sources/Services/Session/UserSessionProtocol.swift index da32bc626..009ff3953 100644 --- a/ElementX/Sources/Services/Session/UserSessionProtocol.swift +++ b/ElementX/Sources/Services/Session/UserSessionProtocol.swift @@ -25,10 +25,11 @@ enum UserSessionCallback { } protocol UserSessionProtocol { - var userID: String { get } - var isSoftLogout: Bool { get } - var deviceId: String? { get } var homeserver: String { get } + var userID: String { get } + var deviceID: String? { get } + + var isSoftLogout: Bool { get } var clientProxy: ClientProxyProtocol { get } var mediaProvider: MediaProviderProtocol { get } diff --git a/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift b/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift index 4e80ce371..423aff26a 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift @@ -244,6 +244,8 @@ class UserSessionFlowCoordinator: CoordinatorProtocol { let userIndicatorController = UserIndicatorController(rootCoordinator: feedbackNavigationStackCoordinator) let parameters = BugReportCoordinatorParameters(bugReportService: bugReportService, + userID: userSession.userID, + deviceID: userSession.deviceID, userIndicatorController: userIndicatorController, screenshot: image, isModallyPresented: true) diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index af4cf0bbf..a2a16e535 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -121,6 +121,8 @@ class MockScreen: Identifiable { case .bugReport: let navigationStackCoordinator = NavigationStackCoordinator() let coordinator = BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), + userID: "@mock:client.com", + deviceID: nil, userIndicatorController: nil, screenshot: nil, isModallyPresented: true)) @@ -129,6 +131,8 @@ class MockScreen: Identifiable { case .bugReportWithScreenshot: let navigationStackCoordinator = NavigationStackCoordinator() let coordinator = BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), + userID: "@mock:client.com", + deviceID: nil, userIndicatorController: nil, screenshot: Asset.Images.appLogo.image, isModallyPresented: false)) diff --git a/UnitTests/Sources/BugReportServiceTests.swift b/UnitTests/Sources/BugReportServiceTests.swift index b8738bb4a..91afdae0e 100644 --- a/UnitTests/Sources/BugReportServiceTests.swift +++ b/UnitTests/Sources/BugReportServiceTests.swift @@ -26,7 +26,9 @@ class BugReportServiceTests: XCTestCase { } func testSubmitBugReportWithMockService() async throws { - let bugReport = BugReport(text: "i cannot send message", + let bugReport = BugReport(userID: "@mock:client.com", + deviceID: nil, + text: "i cannot send message", includeLogs: true, includeCrashLog: true, githubLabels: [], @@ -49,7 +51,9 @@ class BugReportServiceTests: XCTestCase { applicationId: "mock_app_id", session: .mock) - let bugReport = BugReport(text: "i cannot send message", + let bugReport = BugReport(userID: "@mock:client.com", + deviceID: nil, + text: "i cannot send message", includeLogs: true, includeCrashLog: true, githubLabels: [], diff --git a/UnitTests/Sources/BugReportViewModelTests.swift b/UnitTests/Sources/BugReportViewModelTests.swift index 52ac3a6b2..d5a0875cd 100644 --- a/UnitTests/Sources/BugReportViewModelTests.swift +++ b/UnitTests/Sources/BugReportViewModelTests.swift @@ -21,25 +21,36 @@ import XCTest @MainActor class BugReportViewModelTests: XCTestCase { func testInitialState() { - let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), screenshot: nil, isModallyPresented: false) + let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), + userID: "@mock.client.com", + deviceID: nil, + screenshot: nil, + isModallyPresented: false) let context = viewModel.context - + XCTAssertEqual(context.reportText, "") XCTAssertNil(context.viewState.screenshot) XCTAssertTrue(context.sendingLogsEnabled) } - + func testClearScreenshot() async throws { - let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), screenshot: UIImage.actions, isModallyPresented: false) + let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), + userID: "@mock.client.com", + deviceID: nil, + screenshot: UIImage.actions, + isModallyPresented: false) let context = viewModel.context - + context.send(viewAction: .removeScreenshot) try await Task.sleep(nanoseconds: 100_000_000) XCTAssertNil(context.viewState.screenshot) } - + func testAttachScreenshot() async throws { - let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), screenshot: nil, isModallyPresented: false) + let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), + userID: "@mock.client.com", + deviceID: nil, + screenshot: nil, isModallyPresented: false) let context = viewModel.context XCTAssertNil(context.viewState.screenshot) context.send(viewAction: .attachScreenshot(UIImage.actions))