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
This commit is contained in:
Stefan Ceriu
2023-02-24 14:51:36 +02:00
committed by GitHub
parent 252a5f2bdc
commit a3abde9e23
15 changed files with 79 additions and 23 deletions
@@ -235,7 +235,7 @@ class AppCoordinator: AppCoordinatorProtocol {
let credentials = SoftLogoutCredentials(userId: userSession.userID, let credentials = SoftLogoutCredentials(userId: userSession.userID,
homeserverName: userSession.homeserver, homeserverName: userSession.homeserver,
userDisplayName: displayName, userDisplayName: displayName,
deviceId: userSession.deviceId) deviceId: userSession.deviceID)
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore) let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore)
_ = await authenticationService.configure(for: userSession.homeserver) _ = await authenticationService.configure(for: userSession.homeserver)
@@ -23,6 +23,9 @@ enum BugReportCoordinatorResult {
struct BugReportCoordinatorParameters { struct BugReportCoordinatorParameters {
let bugReportService: BugReportServiceProtocol let bugReportService: BugReportServiceProtocol
let userID: String
let deviceID: String?
weak var userIndicatorController: UserIndicatorControllerProtocol? weak var userIndicatorController: UserIndicatorControllerProtocol?
let screenshot: UIImage? let screenshot: UIImage?
let isModallyPresented: Bool let isModallyPresented: Bool
@@ -38,6 +41,8 @@ final class BugReportCoordinator: CoordinatorProtocol {
self.parameters = parameters self.parameters = parameters
viewModel = BugReportViewModel(bugReportService: parameters.bugReportService, viewModel = BugReportViewModel(bugReportService: parameters.bugReportService,
userID: parameters.userID,
deviceID: parameters.deviceID,
screenshot: parameters.screenshot, screenshot: parameters.screenshot,
isModallyPresented: parameters.isModallyPresented) isModallyPresented: parameters.isModallyPresented)
} }
@@ -19,14 +19,21 @@ import SwiftUI
typealias BugReportViewModelType = StateStoreViewModel<BugReportViewState, BugReportViewAction> typealias BugReportViewModelType = StateStoreViewModel<BugReportViewState, BugReportViewAction>
class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol { class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol {
let bugReportService: BugReportServiceProtocol private let bugReportService: BugReportServiceProtocol
private let userID: String
private let deviceID: String?
var callback: ((BugReportViewModelAction) -> Void)? var callback: ((BugReportViewModelAction) -> Void)?
init(bugReportService: BugReportServiceProtocol, init(bugReportService: BugReportServiceProtocol,
userID: String,
deviceID: String?,
screenshot: UIImage?, screenshot: UIImage?,
isModallyPresented: Bool) { isModallyPresented: Bool) {
self.bugReportService = bugReportService self.bugReportService = bugReportService
self.userID = userID
self.deviceID = deviceID
let bindings = BugReportViewStateBindings(reportText: "", sendingLogsEnabled: true) let bindings = BugReportViewStateBindings(reportText: "", sendingLogsEnabled: true)
super.init(initialViewState: BugReportViewState(screenshot: screenshot, super.init(initialViewState: BugReportViewState(screenshot: screenshot,
bindings: bindings, bindings: bindings,
@@ -61,7 +68,9 @@ class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol {
try pngData?.write(to: imageURL) try pngData?.write(to: imageURL)
files.append(imageURL) files.append(imageURL)
} }
let bugReport = BugReport(text: context.reportText, let bugReport = BugReport(userID: userID,
deviceID: deviceID,
text: context.reportText,
includeLogs: context.sendingLogsEnabled, includeLogs: context.sendingLogsEnabled,
includeCrashLog: true, includeCrashLog: true,
githubLabels: [], githubLabels: [],
@@ -173,12 +173,16 @@ struct BugReportScreen: View {
struct BugReport_Previews: PreviewProvider { struct BugReport_Previews: PreviewProvider {
static let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), static let viewModel = BugReportViewModel(bugReportService: MockBugReportService(),
userID: "@mock.client.com",
deviceID: nil,
screenshot: nil, screenshot: nil,
isModallyPresented: false) isModallyPresented: false)
static var previews: some View { static var previews: some View {
NavigationStack { NavigationStack {
BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(), BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(),
userID: "@mock.client.com",
deviceID: nil,
screenshot: nil, screenshot: nil,
isModallyPresented: false).context) isModallyPresented: false).context)
.previewDisplayName("Without Screenshot") .previewDisplayName("Without Screenshot")
@@ -186,6 +190,8 @@ struct BugReport_Previews: PreviewProvider {
NavigationStack { NavigationStack {
BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(), BugReportScreen(context: BugReportViewModel(bugReportService: MockBugReportService(),
userID: "@mock.client.com",
deviceID: nil,
screenshot: Asset.Images.appLogo.image, screenshot: Asset.Images.appLogo.image,
isModallyPresented: false).context) isModallyPresented: false).context)
.previewDisplayName("With Screenshot") .previewDisplayName("With Screenshot")
@@ -78,6 +78,8 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
private func presentBugReportScreen() { private func presentBugReportScreen() {
let params = BugReportCoordinatorParameters(bugReportService: parameters.bugReportService, let params = BugReportCoordinatorParameters(bugReportService: parameters.bugReportService,
userID: parameters.userSession.userID,
deviceID: parameters.userSession.deviceID,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
screenshot: nil, screenshot: nil,
isModallyPresented: false) isModallyPresented: false)
@@ -28,7 +28,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
self.userSession = userSession self.userSession = userSession
let bindings = SettingsScreenViewStateBindings(timelineStyle: ServiceLocator.shared.settings.timelineStyle) let bindings = SettingsScreenViewStateBindings(timelineStyle: ServiceLocator.shared.settings.timelineStyle)
super.init(initialViewState: .init(bindings: bindings, super.init(initialViewState: .init(bindings: bindings,
deviceID: userSession.deviceId, deviceID: userSession.deviceID,
userID: userSession.userID, userID: userSession.userID,
showSessionVerificationSection: !(userSession.sessionVerificationController?.isVerified ?? false), showSessionVerificationSection: !(userSession.sessionVerificationController?.isVerified ?? false),
showDeveloperOptions: ServiceLocator.shared.settings.canShowDeveloperOptions), showDeveloperOptions: ServiceLocator.shared.settings.canShowDeveloperOptions),
@@ -78,10 +78,19 @@ class BugReportService: NSObject, BugReportServiceProtocol {
SentrySDK.crash() SentrySDK.crash()
} }
func submitBugReport(_ bugReport: BugReport, // swiftlint:disable:next function_body_length
progressListener: ProgressListener?) async throws -> SubmitBugReportResponse { func submitBugReport(_ bugReport: BugReport, progressListener: ProgressListener?) async throws -> SubmitBugReportResponse {
var params = [MultipartFormData(key: "text", type: .text(value: bugReport.text))] 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) params.append(contentsOf: defaultParams)
for label in bugReport.githubLabels { for label in bugReport.githubLabels {
params.append(MultipartFormData(key: "label", type: .text(value: label))) 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: "user_language", type: .text(value: Bundle.elementLanguage ?? "null")),
MultipartFormData(key: "fallback_language", type: .text(value: Bundle.elementFallbackLanguage ?? "null")), MultipartFormData(key: "fallback_language", type: .text(value: Bundle.elementFallbackLanguage ?? "null")),
MultipartFormData(key: "local_time", type: .text(value: localTime)), 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))
] ]
} }
@@ -18,6 +18,8 @@ import Foundation
import UIKit import UIKit
struct BugReport { struct BugReport {
let userID: String
let deviceID: String?
let text: String let text: String
let includeLogs: Bool let includeLogs: Bool
let includeCrashLog: Bool let includeCrashLog: Bool
@@ -21,7 +21,7 @@ struct MockUserSession: UserSessionProtocol {
let sessionVerificationController: SessionVerificationControllerProxyProtocol? = nil let sessionVerificationController: SessionVerificationControllerProxyProtocol? = nil
var userID: String { clientProxy.userID } var userID: String { clientProxy.userID }
var isSoftLogout: Bool { clientProxy.isSoftLogout } var isSoftLogout: Bool { clientProxy.isSoftLogout }
var deviceId: String? { clientProxy.deviceId } var deviceID: String? { clientProxy.deviceId }
var homeserver: String { clientProxy.homeserver } var homeserver: String { clientProxy.homeserver }
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
@@ -25,7 +25,7 @@ class UserSession: UserSessionProtocol {
var userID: String { clientProxy.userID } var userID: String { clientProxy.userID }
var isSoftLogout: Bool { clientProxy.isSoftLogout } var isSoftLogout: Bool { clientProxy.isSoftLogout }
var deviceId: String? { clientProxy.deviceId } var deviceID: String? { clientProxy.deviceId }
var homeserver: String { clientProxy.homeserver } var homeserver: String { clientProxy.homeserver }
let clientProxy: ClientProxyProtocol let clientProxy: ClientProxyProtocol
@@ -25,10 +25,11 @@ enum UserSessionCallback {
} }
protocol UserSessionProtocol { protocol UserSessionProtocol {
var userID: String { get }
var isSoftLogout: Bool { get }
var deviceId: String? { get }
var homeserver: String { get } var homeserver: String { get }
var userID: String { get }
var deviceID: String? { get }
var isSoftLogout: Bool { get }
var clientProxy: ClientProxyProtocol { get } var clientProxy: ClientProxyProtocol { get }
var mediaProvider: MediaProviderProtocol { get } var mediaProvider: MediaProviderProtocol { get }
@@ -244,6 +244,8 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
let userIndicatorController = UserIndicatorController(rootCoordinator: feedbackNavigationStackCoordinator) let userIndicatorController = UserIndicatorController(rootCoordinator: feedbackNavigationStackCoordinator)
let parameters = BugReportCoordinatorParameters(bugReportService: bugReportService, let parameters = BugReportCoordinatorParameters(bugReportService: bugReportService,
userID: userSession.userID,
deviceID: userSession.deviceID,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
screenshot: image, screenshot: image,
isModallyPresented: true) isModallyPresented: true)
@@ -121,6 +121,8 @@ class MockScreen: Identifiable {
case .bugReport: case .bugReport:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), let coordinator = BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(),
userID: "@mock:client.com",
deviceID: nil,
userIndicatorController: nil, userIndicatorController: nil,
screenshot: nil, screenshot: nil,
isModallyPresented: true)) isModallyPresented: true))
@@ -129,6 +131,8 @@ class MockScreen: Identifiable {
case .bugReportWithScreenshot: case .bugReportWithScreenshot:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), let coordinator = BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(),
userID: "@mock:client.com",
deviceID: nil,
userIndicatorController: nil, userIndicatorController: nil,
screenshot: Asset.Images.appLogo.image, screenshot: Asset.Images.appLogo.image,
isModallyPresented: false)) isModallyPresented: false))
@@ -26,7 +26,9 @@ class BugReportServiceTests: XCTestCase {
} }
func testSubmitBugReportWithMockService() async throws { 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, includeLogs: true,
includeCrashLog: true, includeCrashLog: true,
githubLabels: [], githubLabels: [],
@@ -49,7 +51,9 @@ class BugReportServiceTests: XCTestCase {
applicationId: "mock_app_id", applicationId: "mock_app_id",
session: .mock) 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, includeLogs: true,
includeCrashLog: true, includeCrashLog: true,
githubLabels: [], githubLabels: [],
@@ -21,7 +21,11 @@ import XCTest
@MainActor @MainActor
class BugReportViewModelTests: XCTestCase { class BugReportViewModelTests: XCTestCase {
func testInitialState() { 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 let context = viewModel.context
XCTAssertEqual(context.reportText, "") XCTAssertEqual(context.reportText, "")
@@ -30,7 +34,11 @@ class BugReportViewModelTests: XCTestCase {
} }
func testClearScreenshot() async throws { 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 let context = viewModel.context
context.send(viewAction: .removeScreenshot) context.send(viewAction: .removeScreenshot)
@@ -39,7 +47,10 @@ class BugReportViewModelTests: XCTestCase {
} }
func testAttachScreenshot() async throws { 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 let context = viewModel.context
XCTAssertNil(context.viewState.screenshot) XCTAssertNil(context.viewState.screenshot)
context.send(viewAction: .attachScreenshot(UIImage.actions)) context.send(viewAction: .attachScreenshot(UIImage.actions))